You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/07/31 21:47:18 UTC

[commons-rng] branch master updated (2e1fc04 -> 63420f6)

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

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


    from 2e1fc04  Specify trusty to allow use of openjdk7 in the build matrix.
     new 789dc71  RNG-110: Add SharedStateDiscreteSampler and SharedStateContinuousSampler
     new 4b43e80  RNG-110: Make distributions samplers implement SharedStateXXXSampler.
     new 376bd5a  RNG-110: Return the specialised delegate from withUniformRandomProvider.
     new 6da7140  RNG-110: PMD rule exceptions to allow Sampler factory constructors.
     new b3a439b  RNG-110: Provide factory constructors for unreleased samplers.
     new 756c251  RNG-110: Provide factory constructors for released samplers.
     new 57ac9f0  RNG-110: Fix javadoc and formatting.
     new 63420f6  RNG-110: Track changes.

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../AliasMethodDiscreteSamplerPerformance.java     |    4 +-
 .../ContinuousSamplersPerformance.java             |   24 +-
 .../distribution/DiscreteSamplersPerformance.java  |   20 +-
 .../distribution/GeometricSamplersPerformance.java |    4 +-
 .../PoissonSamplerCachePerformance.java            |   18 +-
 .../distribution/PoissonSamplersPerformance.java   |    6 +-
 .../commons/rng/examples/jpms/lib/DiceGame.java    |    2 +-
 .../ProbabilityDensityApproximationCommand.java    |   38 +-
 .../UniformSamplingVisualCheckCommand.java         |    6 +-
 .../commons/rng/sampling/UnitSphereSampler.java    |    4 +-
 .../AhrensDieterExponentialSampler.java            |   18 +-
 .../AhrensDieterMarsagliaTsangGammaSampler.java    |   58 +-
 .../distribution/AliasMethodDiscreteSampler.java   |   27 +-
 .../distribution/BoxMullerLogNormalSampler.java    |    4 +-
 .../BoxMullerNormalizedGaussianSampler.java        |   18 +-
 .../sampling/distribution/ChengBetaSampler.java    |   20 +-
 .../distribution/ContinuousUniformSampler.java     |   19 +-
 .../distribution/DiscreteUniformSampler.java       |   67 +-
 .../rng/sampling/distribution/GaussianSampler.java |   24 +-
 .../sampling/distribution/GeometricSampler.java    |   63 +-
 .../distribution/GuideTableDiscreteSampler.java    |  161 +--
 .../InverseTransformContinuousSampler.java         |   22 +-
 .../InverseTransformDiscreteSampler.java           |   22 +-
 .../InverseTransformParetoSampler.java             |   20 +-
 .../distribution/KempSmallMeanPoissonSampler.java  |   67 +-
 .../distribution/LargeMeanPoissonSampler.java      |   64 +-
 .../sampling/distribution/LogNormalSampler.java    |   24 +-
 .../MarsagliaNormalizedGaussianSampler.java        |   18 +-
 .../MarsagliaTsangWangDiscreteSampler.java         | 1070 ++++++++++----------
 .../rng/sampling/distribution/PoissonSampler.java  |   52 +-
 .../sampling/distribution/PoissonSamplerCache.java |    6 +-
 .../RejectionInversionZipfSampler.java             |   21 +-
 ...pler.java => SharedStateContinuousSampler.java} |   17 +-
 ...ampler.java => SharedStateDiscreteSampler.java} |   17 +-
 .../distribution/SmallMeanPoissonSampler.java      |   20 +-
 .../ZigguratNormalizedGaussianSampler.java         |   18 +-
 .../AhrensDieterExponentialSamplerTest.java        |   10 +-
 ...AhrensDieterMarsagliaTsangGammaSamplerTest.java |   27 +-
 .../AliasMethodDiscreteSamplerTest.java            |   16 +-
 .../BoxMullerNormalisedGaussianSamplerTest.java    |    6 +-
 .../distribution/ChengBetaSamplerTest.java         |   14 +-
 .../distribution/ContinuousSamplersList.java       |   44 +-
 .../distribution/ContinuousUniformSamplerTest.java |    8 +-
 .../distribution/DiscreteSamplersList.java         |   42 +-
 .../distribution/DiscreteUniformSamplerTest.java   |   21 +-
 .../sampling/distribution/GaussianSamplerTest.java |   22 +-
 .../distribution/GeometricSamplerTest.java         |   20 +-
 .../GuideTableDiscreteSamplerTest.java             |   14 +-
 .../InverseTransformContinuousSamplerTest.java     |    6 +-
 .../InverseTransformDiscreteSamplerTest.java       |    6 +-
 .../InverseTransformParetoSamplerTest.java         |   14 +-
 .../KempSmallMeanPoissonSamplerTest.java           |   18 +-
 .../distribution/LargeMeanPoissonSamplerTest.java  |   24 +-
 .../distribution/LogNormalSamplerTest.java         |   28 +-
 .../MarsagliaNormalisedGaussianSamplerTest.java    |    6 +-
 .../MarsagliaTsangWangDiscreteSamplerTest.java     |   62 +-
 .../distribution/PoissonSamplerCacheTest.java      |    2 +-
 .../sampling/distribution/PoissonSamplerTest.java  |   17 +-
 .../RejectionInversionZipfSamplerTest.java         |   14 +-
 .../distribution/SmallMeanPoissonSamplerTest.java  |   14 +-
 .../ZigguratNormalizedGaussianSamplerTest.java     |    8 +-
 .../rng/simple/ThreadLocalRandomSource.java        |    4 +-
 src/changes/changes.xml                            |    4 +
 src/main/resources/pmd/pmd-ruleset.xml             |    8 +-
 64 files changed, 1402 insertions(+), 1140 deletions(-)
 copy commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/{ContinuousSampler.java => SharedStateContinuousSampler.java} (69%)
 copy commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/{ContinuousSampler.java => SharedStateDiscreteSampler.java} (70%)


[commons-rng] 03/08: RNG-110: Return the specialised delegate from withUniformRandomProvider.

Posted by ah...@apache.org.
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

commit 376bd5aa516171c00a70ebd32d1ecbe8925ef1b8
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jul 19 21:42:33 2019 +0100

    RNG-110: Return the specialised delegate from withUniformRandomProvider.
---
 .../AhrensDieterMarsagliaTsangGammaSampler.java            | 14 ++------------
 .../rng/sampling/distribution/DiscreteUniformSampler.java  | 14 ++------------
 .../rng/sampling/distribution/GeometricSampler.java        | 11 ++---------
 .../commons/rng/sampling/distribution/PoissonSampler.java  | 13 ++-----------
 4 files changed, 8 insertions(+), 44 deletions(-)

diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
index 006581d..24aa406 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
@@ -282,17 +282,6 @@ public class AhrensDieterMarsagliaTsangGammaSampler
             new MarsagliaTsangGammaSampler(rng, alpha, theta);
     }
 
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param source Source to copy.
-     */
-    @SuppressWarnings("unchecked")
-    private AhrensDieterMarsagliaTsangGammaSampler(UniformRandomProvider rng,
-                                                   AhrensDieterMarsagliaTsangGammaSampler source) {
-        super(null);
-        delegate = source.delegate.withUniformRandomProvider(rng);
-    }
-
     /** {@inheritDoc} */
     @Override
     public double sample() {
@@ -308,6 +297,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
     /** {@inheritDoc} */
     @Override
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        return new AhrensDieterMarsagliaTsangGammaSampler(rng, this);
+        // Direct return of the optimised sampler
+        return delegate.withUniformRandomProvider(rng);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
index bc1830c..b9f6fad 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
@@ -161,17 +161,6 @@ public class DiscreteUniformSampler
             new SmallRangeDiscreteUniformSampler(rng, lower, range);
     }
 
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param source Source to copy.
-     */
-    @SuppressWarnings("unchecked")
-    private DiscreteUniformSampler(UniformRandomProvider rng,
-                                   DiscreteUniformSampler source) {
-        super(null);
-        delegate = source.delegate.withUniformRandomProvider(rng);
-    }
-
     /** {@inheritDoc} */
     @Override
     public int sample() {
@@ -187,6 +176,7 @@ public class DiscreteUniformSampler
     /** {@inheritDoc} */
     @Override
     public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        return new DiscreteUniformSampler(rng, this);
+        // Direct return of the optimised sampler
+        return delegate.withUniformRandomProvider(rng);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
index a65a46e..25a4703 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
@@ -154,14 +154,6 @@ public class GeometricSampler implements SharedStateDiscreteSampler {
     }
 
     /**
-     * @param rng Generator of uniformly distributed random numbers
-     * @param source Source to copy.
-     */
-    private GeometricSampler(UniformRandomProvider rng, GeometricSampler source) {
-        delegate = source.delegate.withUniformRandomProvider(rng);
-    }
-
-    /**
      * Create a sample from a geometric distribution.
      *
      * <p>The sample will take the values in the set {@code [0, 1, 2, ...]}, equivalent to the
@@ -181,6 +173,7 @@ public class GeometricSampler implements SharedStateDiscreteSampler {
     /** {@inheritDoc} */
     @Override
     public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        return new GeometricSampler(rng, this);
+        // Direct return of the optimised sampler
+        return delegate.withUniformRandomProvider(rng);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
index 36cc04b..6834db9 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
@@ -79,16 +79,6 @@ public class PoissonSampler
             new LargeMeanPoissonSampler(rng, mean);
     }
 
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param source Source to copy.
-     */
-    private PoissonSampler(UniformRandomProvider rng,
-                           PoissonSampler source) {
-        super(null);
-        poissonSamplerDelegate = source.poissonSamplerDelegate.withUniformRandomProvider(rng);
-    }
-
     /** {@inheritDoc} */
     @Override
     public int sample() {
@@ -104,6 +94,7 @@ public class PoissonSampler
     /** {@inheritDoc} */
     @Override
     public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        return new PoissonSampler(rng, this);
+        // Direct return of the optimised sampler
+        return poissonSamplerDelegate.withUniformRandomProvider(rng);
     }
 }


[commons-rng] 07/08: RNG-110: Fix javadoc and formatting.

Posted by ah...@apache.org.
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

commit 57ac9f078a43ef062f2a4faa6a78462f8ed42e05
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Tue Jul 30 21:13:34 2019 +0100

    RNG-110: Fix javadoc and formatting.
---
 .../sampling/distribution/AhrensDieterExponentialSampler.java  |  2 +-
 .../distribution/AhrensDieterMarsagliaTsangGammaSampler.java   |  8 ++++----
 .../rng/sampling/distribution/BoxMullerLogNormalSampler.java   |  2 +-
 .../commons/rng/sampling/distribution/GeometricSampler.java    | 10 +++++-----
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
index e8ad681..b6d23d3 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
@@ -143,7 +143,7 @@ public class AhrensDieterExponentialSampler
     }
 
     /**
-     * Create a new Exponential distribution sampler.
+     * Create a new exponential distribution sampler.
      *
      * @param rng Generator of uniformly distributed random numbers.
      * @param mean Mean of the distribution.
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
index db52483..dc565f0 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
@@ -19,7 +19,7 @@ package org.apache.commons.rng.sampling.distribution;
 import org.apache.commons.rng.UniformRandomProvider;
 
 /**
- * Sampling from the <a href="http://mathworld.wolfram.com/GammaDistribution.html">Gamma distribution</a>.
+ * Sampling from the <a href="http://mathworld.wolfram.com/GammaDistribution.html">gamma distribution</a>.
  * <ul>
  *  <li>
  *   For {@code 0 < alpha < 1}:
@@ -303,7 +303,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
     }
 
     /**
-     * Creates a new Gamma distribution sampler.
+     * Creates a new gamma distribution sampler.
      *
      * @param rng Generator of uniformly distributed random numbers.
      * @param alpha Alpha parameter of the distribution (this is a shape parameter).
@@ -312,8 +312,8 @@ public class AhrensDieterMarsagliaTsangGammaSampler
      * @throws IllegalArgumentException if {@code alpha <= 0} or {@code theta <= 0}
      */
     public static SharedStateContinuousSampler of(UniformRandomProvider rng,
-                                                double alpha,
-                                                double theta) {
+                                                  double alpha,
+                                                  double theta) {
         // Each sampler should check the input arguments.
         return alpha < 1 ?
                 new AhrensDieterGammaSampler(rng, alpha, theta) :
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java
index d4177d1..dae2155 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java
@@ -47,7 +47,7 @@ public class BoxMullerLogNormalSampler
                                      double shape) {
         super(null);
         sampler = LogNormalSampler.of(BoxMullerNormalizedGaussianSampler.of(rng),
-                                       scale, shape);
+                                      scale, shape);
     }
 
     /** {@inheritDoc} */
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
index a3f3339..e701e72 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
@@ -133,15 +133,15 @@ public final class GeometricSampler {
     private GeometricSampler() {}
 
     /**
-     * Creates a new geometric distribution sampler. The samples will be provided in
-     * the set {@code k=[0, 1, 2, ...]} where {@code k} indicates the number of
-     * failures before the first success.
+     * Creates a new geometric distribution sampler. The samples will be provided in the set
+     * {@code k=[0, 1, 2, ...]} where {@code k} indicates the number of failures before the first
+     * success.
      *
      * @param rng Generator of uniformly distributed random numbers.
      * @param probabilityOfSuccess The probability of success.
      * @return the sampler
-     * @throws IllegalArgumentException if {@code probabilityOfSuccess} is not in
-     * the range {@code [0 < probabilityOfSuccess <= 1]})
+     * @throws IllegalArgumentException if {@code probabilityOfSuccess} is not in the range
+     * {@code [0 < probabilityOfSuccess <= 1]})
      */
     public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
                                                 double probabilityOfSuccess) {


[commons-rng] 06/08: RNG-110: Provide factory constructors for released samplers.

Posted by ah...@apache.org.
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

commit 756c2512e788975384e1f6ea76744424f0dfd911
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Sun Jul 21 11:50:31 2019 +0100

    RNG-110: Provide factory constructors for released samplers.
    
    These samplers must maintain an instance constructor for binary
    compatibility.
---
 .../ContinuousSamplersPerformance.java             | 24 ++++++------
 .../distribution/DiscreteSamplersPerformance.java  |  8 ++--
 .../distribution/GeometricSamplersPerformance.java |  2 +-
 .../PoissonSamplerCachePerformance.java            | 18 +++++----
 .../distribution/PoissonSamplersPerformance.java   |  4 +-
 .../commons/rng/examples/jpms/lib/DiceGame.java    |  2 +-
 .../ProbabilityDensityApproximationCommand.java    | 38 +++++++++----------
 .../UniformSamplingVisualCheckCommand.java         |  6 +--
 .../commons/rng/sampling/UnitSphereSampler.java    |  4 +-
 .../AhrensDieterExponentialSampler.java            | 13 +++++++
 .../AhrensDieterMarsagliaTsangGammaSampler.java    | 29 +++++++++++---
 .../distribution/BoxMullerLogNormalSampler.java    |  2 +-
 .../BoxMullerNormalizedGaussianSampler.java        | 13 +++++++
 .../sampling/distribution/ChengBetaSampler.java    | 15 ++++++++
 .../distribution/ContinuousUniformSampler.java     | 16 +++++++-
 .../distribution/DiscreteUniformSampler.java       | 40 ++++++++++++++------
 .../rng/sampling/distribution/GaussianSampler.java | 20 ++++++++++
 .../sampling/distribution/GeometricSampler.java    |  2 +-
 .../InverseTransformContinuousSampler.java         | 17 +++++++++
 .../InverseTransformDiscreteSampler.java           | 17 +++++++++
 .../InverseTransformParetoSampler.java             | 15 ++++++++
 .../distribution/LargeMeanPoissonSampler.java      | 21 +++++++++--
 .../sampling/distribution/LogNormalSampler.java    | 20 ++++++++++
 .../MarsagliaNormalizedGaussianSampler.java        | 13 +++++++
 .../rng/sampling/distribution/PoissonSampler.java  | 25 ++++++++++--
 .../sampling/distribution/PoissonSamplerCache.java |  6 +--
 .../RejectionInversionZipfSampler.java             | 16 ++++++++
 .../distribution/SmallMeanPoissonSampler.java      | 15 +++++++-
 .../ZigguratNormalizedGaussianSampler.java         | 13 +++++++
 .../AhrensDieterExponentialSamplerTest.java        |  8 ++--
 ...AhrensDieterMarsagliaTsangGammaSamplerTest.java | 25 ++++++++----
 .../BoxMullerNormalisedGaussianSamplerTest.java    |  4 +-
 .../distribution/ChengBetaSamplerTest.java         | 12 ++----
 .../distribution/ContinuousSamplersList.java       | 44 +++++++++++-----------
 .../distribution/ContinuousUniformSamplerTest.java |  6 +--
 .../distribution/DiscreteSamplersList.java         | 22 +++++------
 .../distribution/DiscreteUniformSamplerTest.java   | 19 ++++++++--
 .../sampling/distribution/GaussianSamplerTest.java | 20 +++++-----
 .../InverseTransformContinuousSamplerTest.java     |  4 +-
 .../InverseTransformDiscreteSamplerTest.java       |  4 +-
 .../InverseTransformParetoSamplerTest.java         | 12 ++----
 .../distribution/LargeMeanPoissonSamplerTest.java  | 22 +++++------
 .../distribution/LogNormalSamplerTest.java         | 26 ++++++-------
 .../MarsagliaNormalisedGaussianSamplerTest.java    |  4 +-
 .../distribution/PoissonSamplerCacheTest.java      |  2 +-
 .../sampling/distribution/PoissonSamplerTest.java  | 15 +++++++-
 .../RejectionInversionZipfSamplerTest.java         | 12 ++----
 .../distribution/SmallMeanPoissonSamplerTest.java  | 12 +++---
 .../ZigguratNormalizedGaussianSamplerTest.java     |  6 +--
 .../rng/simple/ThreadLocalRandomSource.java        |  4 +-
 50 files changed, 496 insertions(+), 221 deletions(-)

diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/ContinuousSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/ContinuousSamplersPerformance.java
index cb311cb..fea6cfa 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/ContinuousSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/ContinuousSamplersPerformance.java
@@ -97,31 +97,31 @@ public class ContinuousSamplersPerformance {
             super.setup();
             final UniformRandomProvider rng = getGenerator();
             if ("BoxMullerNormalizedGaussianSampler".equals(samplerType)) {
-                sampler = new BoxMullerNormalizedGaussianSampler(rng);
+                sampler = BoxMullerNormalizedGaussianSampler.of(rng);
             } else if ("MarsagliaNormalizedGaussianSampler".equals(samplerType)) {
-                sampler = new MarsagliaNormalizedGaussianSampler(rng);
+                sampler = MarsagliaNormalizedGaussianSampler.of(rng);
             } else if ("ZigguratNormalizedGaussianSampler".equals(samplerType)) {
-                sampler = new ZigguratNormalizedGaussianSampler(rng);
+                sampler = ZigguratNormalizedGaussianSampler.of(rng);
             } else if ("AhrensDieterExponentialSampler".equals(samplerType)) {
-                sampler = new AhrensDieterExponentialSampler(rng, 4.56);
+                sampler = AhrensDieterExponentialSampler.of(rng, 4.56);
             } else if ("AhrensDieterGammaSampler".equals(samplerType)) {
                 // This tests the Ahrens-Dieter algorithm since alpha < 1
-                sampler = new AhrensDieterMarsagliaTsangGammaSampler(rng, 0.76, 9.8);
+                sampler = AhrensDieterMarsagliaTsangGammaSampler.of(rng, 0.76, 9.8);
             } else if ("MarsagliaTsangGammaSampler".equals(samplerType)) {
                 // This tests the Marsaglia-Tsang algorithm since alpha > 1
-                sampler = new AhrensDieterMarsagliaTsangGammaSampler(rng, 12.34, 9.8);
+                sampler = AhrensDieterMarsagliaTsangGammaSampler.of(rng, 12.34, 9.8);
             } else if ("LogNormalBoxMullerNormalizedGaussianSampler".equals(samplerType)) {
-                sampler = new LogNormalSampler(new BoxMullerNormalizedGaussianSampler(rng), 12.3, 4.6);
+                sampler = LogNormalSampler.of(BoxMullerNormalizedGaussianSampler.of(rng), 12.3, 4.6);
             } else if ("LogNormalMarsagliaNormalizedGaussianSampler".equals(samplerType)) {
-                sampler = new LogNormalSampler(new MarsagliaNormalizedGaussianSampler(rng), 12.3, 4.6);
+                sampler = LogNormalSampler.of(MarsagliaNormalizedGaussianSampler.of(rng), 12.3, 4.6);
             } else if ("LogNormalZigguratNormalizedGaussianSampler".equals(samplerType)) {
-                sampler = new LogNormalSampler(new ZigguratNormalizedGaussianSampler(rng), 12.3, 4.6);
+                sampler = LogNormalSampler.of(ZigguratNormalizedGaussianSampler.of(rng), 12.3, 4.6);
             } else if ("ChengBetaSampler".equals(samplerType)) {
-                sampler = new ChengBetaSampler(rng, 0.45, 6.7);
+                sampler = ChengBetaSampler.of(rng, 0.45, 6.7);
             } else if ("ContinuousUniformSampler".equals(samplerType)) {
-                sampler = new ContinuousUniformSampler(rng, 123.4, 5678.9);
+                sampler = ContinuousUniformSampler.of(rng, 123.4, 5678.9);
             } else if ("InverseTransformParetoSampler".equals(samplerType)) {
-                sampler = new InverseTransformParetoSampler(rng, 23.45, 0.1234);
+                sampler = InverseTransformParetoSampler.of(rng, 23.45, 0.1234);
             }
         }
     }
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
index 03bae18..ef41587 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
@@ -110,14 +110,14 @@ public class DiscreteSamplersPerformance {
             super.setup();
             final UniformRandomProvider rng = getGenerator();
             if ("DiscreteUniformSampler".equals(samplerType)) {
-                sampler = new DiscreteUniformSampler(rng, -98, 76);
+                sampler = DiscreteUniformSampler.of(rng, -98, 76);
             } else if ("RejectionInversionZipfSampler".equals(samplerType)) {
-                sampler = new RejectionInversionZipfSampler(rng, 43, 2.1);
+                sampler = RejectionInversionZipfSampler.of(rng, 43, 2.1);
             } else if ("SmallMeanPoissonSampler".equals(samplerType)) {
-                sampler = new SmallMeanPoissonSampler(rng, 8.9);
+                sampler = SmallMeanPoissonSampler.of(rng, 8.9);
             } else if ("LargeMeanPoissonSampler".equals(samplerType)) {
                 // Note: Use with a fractional part to the mean includes a small mean sample
-                sampler = new LargeMeanPoissonSampler(rng, 41.7);
+                sampler = LargeMeanPoissonSampler.of(rng, 41.7);
             } else if ("GeometricSampler".equals(samplerType)) {
                 sampler = GeometricSampler.of(rng, 0.21);
             } else if ("MarsagliaTsangWangDiscreteSampler".equals(samplerType)) {
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
index 7fc3e57..954a3bd 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
@@ -99,7 +99,7 @@ public class GeometricSamplersPerformance {
             } else {
                 final DiscreteInverseCumulativeProbabilityFunction geometricFunction =
                     new GeometricDiscreteInverseCumulativeProbabilityFunction(probabilityOfSuccess);
-                sampler = new InverseTransformDiscreteSampler(rng, geometricFunction);
+                sampler = InverseTransformDiscreteSampler.of(rng, geometricFunction);
             }
         }
     }
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplerCachePerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplerCachePerformance.java
index 7311cde..efe0113 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplerCachePerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplerCachePerformance.java
@@ -45,7 +45,7 @@ import org.openjdk.jmh.infra.Blackhole;
  * cache.
  *
  * <p>The benchmark is designed for a worse case scenario of Poisson means that are uniformly spread
- * over a range and non-integer. A single sample is required per mean, E.g.
+ * over a range and non-integer. A single sample is required per mean, E.g.</p>
  *
  * <pre>
  * int min = 40;
@@ -55,7 +55,7 @@ import org.openjdk.jmh.infra.Blackhole;
  *
  * // Compare ...
  * for (int i = 0; i &lt; 1000; i++) {
- *   new PoissonSampler(rng, min + rng.nextDouble() * range).sample();
+ *   PoissonSampler.of(rng, min + rng.nextDouble() * range).sample();
  * }
  *
  * // To ...
@@ -67,7 +67,7 @@ import org.openjdk.jmh.infra.Blackhole;
  *
  * <p>The alternative scenario where the means are integer is not considered as this could be easily
  * handled by creating an array to hold the PoissonSamplers for each mean. This does not require any
- * specialised caching of state and is simple enough to perform for single threaded applications:
+ * specialised caching of state and is simple enough to perform for single threaded applications:</p>
  *
  * <pre>
  * public class SimpleUnsafePoissonSamplerCache {
@@ -77,12 +77,12 @@ import org.openjdk.jmh.infra.Blackhole;
  *
  *   public PoissonSampler createPoissonSampler(UniformRandomProvider rng, int mean) {
  *     if (mean &lt; min || mean &gt; max) {
- *       return new PoissonSampler(rng, mean);
+ *       return PoissonSampler.of(rng, mean);
  *     }
  *     int index = mean - min;
  *     PoissonSampler sample = samplers[index];
  *     if (sampler == null) {
- *       sampler = new PoissonSampler(rng, mean);
+ *       sampler = PoissonSampler.of(rng, mean);
  *       samplers[index] = sampler;
  *     }
  *     return sampler;
@@ -90,8 +90,10 @@ import org.openjdk.jmh.infra.Blackhole;
  * }
  * </pre>
  *
- * Note that in this example the UniformRandomProvider is also cached and so this is only
- * applicable to a single threaded application.
+ * <p>Note that in this example the UniformRandomProvider is also cached and so this is only
+ * applicable to a single threaded application. Thread safety could be ensured using the
+ * {@link org.apache.commons.rng.sampling.SharedStateSampler SharedStateSampler} functionality
+ * of the cached sampler.</p>
  *
  * <p>Re-written to use the PoissonSamplerCache would provide a new PoissonSampler per call in a
  * thread-safe manner:
@@ -308,7 +310,7 @@ public class PoissonSamplerCachePerformance {
         final PoissonSamplerFactory factory = new PoissonSamplerFactory() {
             @Override
             public DiscreteSampler createPoissonSampler(double mean) {
-                return new PoissonSampler(r, mean);
+                return PoissonSampler.of(r, mean);
             }
         };
         runSample(factory, range, bh);
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
index 2ef6f8b..4fab0a2 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
@@ -178,7 +178,7 @@ public class PoissonSamplersPerformance {
                 factory = new DiscreteSamplerFactory() {
                     @Override
                     public DiscreteSampler create() {
-                        return new SmallMeanPoissonSampler(generator, mean);
+                        return SmallMeanPoissonSampler.of(generator, mean);
                     }
                 };
             } else if ("KempSmallMeanPoissonSampler".equals(samplerType)) {
@@ -221,7 +221,7 @@ public class PoissonSamplersPerformance {
                     @Override
                     public DiscreteSampler create() {
                         // Note this is not valid when mean < 1
-                        return new LargeMeanPoissonSampler(generator, mean);
+                        return LargeMeanPoissonSampler.of(generator, mean);
                     }
                 };
             } else if ("TinyMeanPoissonSampler".equals(samplerType)) {
diff --git a/commons-rng-examples/examples-jpms/jpms-lib/src/main/java/org/apache/commons/rng/examples/jpms/lib/DiceGame.java b/commons-rng-examples/examples-jpms/jpms-lib/src/main/java/org/apache/commons/rng/examples/jpms/lib/DiceGame.java
index c12d4f0..db91f72 100644
--- a/commons-rng-examples/examples-jpms/jpms-lib/src/main/java/org/apache/commons/rng/examples/jpms/lib/DiceGame.java
+++ b/commons-rng-examples/examples-jpms/jpms-lib/src/main/java/org/apache/commons/rng/examples/jpms/lib/DiceGame.java
@@ -50,7 +50,7 @@ public class DiceGame {
         this.rng = rng;
         this.rounds = rounds;
         this.players = players;
-        sampler = new GaussianSampler(new ZigguratNormalizedGaussianSampler(rng), mu, sigma);
+        sampler = GaussianSampler.of(ZigguratNormalizedGaussianSampler.of(rng), mu, sigma);
     }
 
     /**
diff --git a/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/ProbabilityDensityApproximationCommand.java b/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/ProbabilityDensityApproximationCommand.java
index 5dd0453..8a80405 100644
--- a/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/ProbabilityDensityApproximationCommand.java
+++ b/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/ProbabilityDensityApproximationCommand.java
@@ -187,18 +187,18 @@ class ProbabilityDensityApproximationCommand  implements Callable<Void> {
         final double gaussMin = -9;
         final double gaussMax = 11;
         if (samplers.contains(Sampler.ZigguratGaussianSampler)) {
-            createDensity(new GaussianSampler(new ZigguratNormalizedGaussianSampler(rng),
-                                              gaussMean, gaussSigma),
+            createDensity(GaussianSampler.of(ZigguratNormalizedGaussianSampler.of(rng),
+                                             gaussMean, gaussSigma),
                           gaussMin, gaussMax, "gauss.ziggurat.txt");
         }
         if (samplers.contains(Sampler.MarsagliaGaussianSampler)) {
-            createDensity(new GaussianSampler(new MarsagliaNormalizedGaussianSampler(rng),
-                                              gaussMean, gaussSigma),
+            createDensity(GaussianSampler.of(MarsagliaNormalizedGaussianSampler.of(rng),
+                                             gaussMean, gaussSigma),
                           gaussMin, gaussMax, "gauss.marsaglia.txt");
         }
         if (samplers.contains(Sampler.BoxMullerGaussianSampler)) {
-            createDensity(new GaussianSampler(new BoxMullerNormalizedGaussianSampler(rng),
-                                              gaussMean, gaussSigma),
+            createDensity(GaussianSampler.of(BoxMullerNormalizedGaussianSampler.of(rng),
+                                             gaussMean, gaussSigma),
                           gaussMin, gaussMax, "gauss.boxmuller.txt");
         }
 
@@ -207,13 +207,13 @@ class ProbabilityDensityApproximationCommand  implements Callable<Void> {
         if (samplers.contains(Sampler.ChengBetaSamplerCase1)) {
             final double alphaBeta = 4.3;
             final double betaBeta = 2.1;
-            createDensity(new ChengBetaSampler(rng, alphaBeta, betaBeta),
+            createDensity(ChengBetaSampler.of(rng, alphaBeta, betaBeta),
                           betaMin, betaMax, "beta.case1.txt");
         }
         if (samplers.contains(Sampler.ChengBetaSamplerCase2)) {
             final double alphaBetaAlt = 0.5678;
             final double betaBetaAlt = 0.1234;
-            createDensity(new ChengBetaSampler(rng, alphaBetaAlt, betaBetaAlt),
+            createDensity(ChengBetaSampler.of(rng, alphaBetaAlt, betaBetaAlt),
                           betaMin, betaMax, "beta.case2.txt");
         }
 
@@ -221,7 +221,7 @@ class ProbabilityDensityApproximationCommand  implements Callable<Void> {
             final double meanExp = 3.45;
             final double expMin = 0;
             final double expMax = 60;
-            createDensity(new AhrensDieterExponentialSampler(rng, meanExp),
+            createDensity(AhrensDieterExponentialSampler.of(rng, meanExp),
                           expMin, expMax, "exp.txt");
         }
 
@@ -230,13 +230,13 @@ class ProbabilityDensityApproximationCommand  implements Callable<Void> {
         final double thetaGamma = 3.456;
         if (samplers.contains(Sampler.AhrensDieterMarsagliaTsangGammaSamplerCase1)) {
             final double alphaGammaSmallerThanOne = 0.1234;
-            createDensity(new AhrensDieterMarsagliaTsangGammaSampler(rng, alphaGammaSmallerThanOne, thetaGamma),
+            createDensity(AhrensDieterMarsagliaTsangGammaSampler.of(rng, alphaGammaSmallerThanOne, thetaGamma),
                           gammaMin, gammaMax1, "gamma.case1.txt");
         }
         if (samplers.contains(Sampler.AhrensDieterMarsagliaTsangGammaSamplerCase2)) {
             final double alphaGammaLargerThanOne = 2.345;
             final double gammaMax2 = 70;
-            createDensity(new AhrensDieterMarsagliaTsangGammaSampler(rng, alphaGammaLargerThanOne, thetaGamma),
+            createDensity(AhrensDieterMarsagliaTsangGammaSampler.of(rng, alphaGammaLargerThanOne, thetaGamma),
                           gammaMin, gammaMax2, "gamma.case2.txt");
         }
 
@@ -245,14 +245,14 @@ class ProbabilityDensityApproximationCommand  implements Callable<Void> {
         final double paretoMin = 23;
         final double paretoMax = 400;
         if (samplers.contains(Sampler.InverseTransformParetoSampler)) {
-            createDensity(new InverseTransformParetoSampler(rng, scalePareto, shapePareto),
+            createDensity(InverseTransformParetoSampler.of(rng, scalePareto, shapePareto),
                           paretoMin, paretoMax, "pareto.txt");
         }
 
         final double loUniform = -9.876;
         final double hiUniform = 5.432;
         if (samplers.contains(Sampler.ContinuousUniformSampler)) {
-            createDensity(new ContinuousUniformSampler(rng, loUniform, hiUniform),
+            createDensity(ContinuousUniformSampler.of(rng, loUniform, hiUniform),
                           loUniform, hiUniform, "uniform.txt");
         }
 
@@ -261,18 +261,18 @@ class ProbabilityDensityApproximationCommand  implements Callable<Void> {
         final double logNormalMin = 5;
         final double logNormalMax = 25;
         if (samplers.contains(Sampler.LogNormalZigguratGaussianSampler)) {
-            createDensity(new LogNormalSampler(new ZigguratNormalizedGaussianSampler(rng),
-                                               scaleLogNormal, shapeLogNormal),
+            createDensity(LogNormalSampler.of(ZigguratNormalizedGaussianSampler.of(rng),
+                                              scaleLogNormal, shapeLogNormal),
                           logNormalMin, logNormalMax, "lognormal.ziggurat.txt");
         }
         if (samplers.contains(Sampler.LogNormalMarsagliaGaussianSampler)) {
-            createDensity(new LogNormalSampler(new MarsagliaNormalizedGaussianSampler(rng),
-                                               scaleLogNormal, shapeLogNormal),
+            createDensity(LogNormalSampler.of(MarsagliaNormalizedGaussianSampler.of(rng),
+                                              scaleLogNormal, shapeLogNormal),
                           logNormalMin, logNormalMax, "lognormal.marsaglia.txt");
         }
         if (samplers.contains(Sampler.LogNormalBoxMullerGaussianSampler)) {
-            createDensity(new LogNormalSampler(new BoxMullerNormalizedGaussianSampler(rng),
-                                               scaleLogNormal, shapeLogNormal),
+            createDensity(LogNormalSampler.of(BoxMullerNormalizedGaussianSampler.of(rng),
+                                              scaleLogNormal, shapeLogNormal),
                           logNormalMin, logNormalMax, "lognormal.boxmuller.txt");
         }
 
diff --git a/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/UniformSamplingVisualCheckCommand.java b/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/UniformSamplingVisualCheckCommand.java
index c22b5e4..13a170d 100644
--- a/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/UniformSamplingVisualCheckCommand.java
+++ b/commons-rng-examples/examples-sampling/src/main/java/org/apache/commons/rng/examples/sampling/UniformSamplingVisualCheckCommand.java
@@ -64,9 +64,9 @@ class UniformSamplingVisualCheckCommand implements Callable<Void> {
     private final UniformRandomProvider rng = RandomSource.create(RandomSource.XOR_SHIFT_1024_S_PHI);
     /** Samplers. */
     private final ContinuousSampler[] samplers = new ContinuousSampler[] {
-        new ZigguratNormalizedGaussianSampler(rng),
-        new MarsagliaNormalizedGaussianSampler(rng),
-        new BoxMullerNormalizedGaussianSampler(rng),
+        ZigguratNormalizedGaussianSampler.of(rng),
+        MarsagliaNormalizedGaussianSampler.of(rng),
+        BoxMullerNormalizedGaussianSampler.of(rng),
     };
 
     // Allow System.out
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java
index 67cecd1..10c1702 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java
@@ -53,7 +53,7 @@ public class UnitSphereSampler implements SharedStateSampler<UnitSphereSampler>
         }
 
         this.dimension = dimension;
-        sampler = new ZigguratNormalizedGaussianSampler(rng);
+        sampler = ZigguratNormalizedGaussianSampler.of(rng);
     }
 
     /**
@@ -63,7 +63,7 @@ public class UnitSphereSampler implements SharedStateSampler<UnitSphereSampler>
     private UnitSphereSampler(UniformRandomProvider rng,
                               UnitSphereSampler source) {
         // The Gaussian sampler has no shared state so create a new instance
-        sampler = new ZigguratNormalizedGaussianSampler(rng);
+        sampler = ZigguratNormalizedGaussianSampler.of(rng);
         dimension = source.dimension;
     }
 
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
index 0340a45..e8ad681 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
@@ -141,4 +141,17 @@ public class AhrensDieterExponentialSampler
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new AhrensDieterExponentialSampler(rng, this);
     }
+
+    /**
+     * Create a new Exponential distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param mean Mean of the distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code mean <= 0}
+     */
+    public static SharedStateContinuousSampler of(UniformRandomProvider rng,
+                                                  double mean) {
+        return new AhrensDieterExponentialSampler(rng, mean);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
index 24aa406..db52483 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
@@ -219,7 +219,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
                                    double alpha,
                                    double theta) {
             super(rng, alpha, theta);
-            gaussian = new ZigguratNormalizedGaussianSampler(rng);
+            gaussian = ZigguratNormalizedGaussianSampler.of(rng);
             dOptim = alpha - ONE_THIRD;
             cOptim = ONE_THIRD / Math.sqrt(dOptim);
         }
@@ -231,7 +231,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
         MarsagliaTsangGammaSampler(UniformRandomProvider rng,
                                    MarsagliaTsangGammaSampler source) {
             super(rng, source);
-            gaussian = new ZigguratNormalizedGaussianSampler(rng);
+            gaussian = ZigguratNormalizedGaussianSampler.of(rng);
             dOptim = source.dOptim;
             cOptim = source.cOptim;
         }
@@ -268,6 +268,9 @@ public class AhrensDieterMarsagliaTsangGammaSampler
     }
 
     /**
+     * This instance delegates sampling. Use the factory method
+     * {@link #of(UniformRandomProvider, double, double)} to create an optimal sampler.
+     *
      * @param rng Generator of uniformly distributed random numbers.
      * @param alpha Alpha parameter of the distribution (this is a shape parameter).
      * @param theta Theta parameter of the distribution (this is a scale parameter).
@@ -277,9 +280,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
                                                   double alpha,
                                                   double theta) {
         super(null);
-        delegate = alpha < 1 ?
-            new AhrensDieterGammaSampler(rng, alpha, theta) :
-            new MarsagliaTsangGammaSampler(rng, alpha, theta);
+        delegate = of(rng, alpha, theta);
     }
 
     /** {@inheritDoc} */
@@ -300,4 +301,22 @@ public class AhrensDieterMarsagliaTsangGammaSampler
         // Direct return of the optimised sampler
         return delegate.withUniformRandomProvider(rng);
     }
+
+    /**
+     * Creates a new Gamma distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param alpha Alpha parameter of the distribution (this is a shape parameter).
+     * @param theta Theta parameter of the distribution (this is a scale parameter).
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code alpha <= 0} or {@code theta <= 0}
+     */
+    public static SharedStateContinuousSampler of(UniformRandomProvider rng,
+                                                double alpha,
+                                                double theta) {
+        // Each sampler should check the input arguments.
+        return alpha < 1 ?
+                new AhrensDieterGammaSampler(rng, alpha, theta) :
+                new MarsagliaTsangGammaSampler(rng, alpha, theta);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java
index de8fc29..d4177d1 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerLogNormalSampler.java
@@ -46,7 +46,7 @@ public class BoxMullerLogNormalSampler
                                      double scale,
                                      double shape) {
         super(null);
-        sampler = new LogNormalSampler(new BoxMullerNormalizedGaussianSampler(rng),
+        sampler = LogNormalSampler.of(BoxMullerNormalizedGaussianSampler.of(rng),
                                        scale, shape);
     }
 
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java
index d707670..39422cf 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java
@@ -81,4 +81,17 @@ public class BoxMullerNormalizedGaussianSampler
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new BoxMullerNormalizedGaussianSampler(rng);
     }
+
+    /**
+     * Create a new normalised Gaussian sampler.
+     *
+     * @param <S> Sampler type.
+     * @param rng Generator of uniformly distributed random numbers.
+     * @return the sampler
+     */
+    @SuppressWarnings("unchecked")
+    public static <S extends NormalizedGaussianSampler & SharedStateContinuousSampler> S
+            of(UniformRandomProvider rng) {
+        return (S) new BoxMullerNormalizedGaussianSampler(rng);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java
index 6fe83e7..1a01e1f 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java
@@ -207,4 +207,19 @@ public class ChengBetaSampler
                            double b) {
         return Math.abs(a - b) <= Double.MIN_VALUE;
     }
+
+    /**
+     * Creates a new Beta distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param alpha Distribution first shape parameter.
+     * @param beta Distribution second shape parameter.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code alpha <= 0} or {@code beta <= 0}
+     */
+    public static SharedStateContinuousSampler of(UniformRandomProvider rng,
+                                                  double alpha,
+                                                  double beta) {
+        return new ChengBetaSampler(rng, alpha, beta);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
index 43a7ec9..4aea1ab 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
@@ -64,7 +64,21 @@ public class ContinuousUniformSampler
 
     /** {@inheritDoc} */
     @Override
-    public ContinuousUniformSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        return new ContinuousUniformSampler(rng, lo, hi);
+    }
+
+    /**
+     * Creates a new continuous uniform distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param lo Lower bound.
+     * @param hi Higher bound.
+     * @return the sampler
+     */
+    public static SharedStateContinuousSampler of(UniformRandomProvider rng,
+                                                  double lo,
+                                                  double hi) {
         return new ContinuousUniformSampler(rng, lo, hi);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
index b9f6fad..2814799 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
@@ -139,6 +139,9 @@ public class DiscreteUniformSampler
     }
 
     /**
+     * This instance delegates sampling. Use the factory method
+     * {@link #of(UniformRandomProvider, int, int)} to create an optimal sampler.
+     *
      * @param rng Generator of uniformly distributed random numbers.
      * @param lower Lower bound (inclusive) of the distribution.
      * @param upper Upper bound (inclusive) of the distribution.
@@ -148,17 +151,7 @@ public class DiscreteUniformSampler
                                   int lower,
                                   int upper) {
         super(null);
-        if (lower > upper) {
-            throw new IllegalArgumentException(lower  + " > " + upper);
-        }
-        // Choose the algorithm depending on the range
-        final int range = (upper - lower) + 1;
-        delegate = range <= 0 ?
-            // The range is too wide to fit in a positive int (larger
-            // than 2^31); use a simple rejection method.
-            new LargeRangeDiscreteUniformSampler(rng, lower, upper) :
-            // Use a sample from the range added to the lower bound.
-            new SmallRangeDiscreteUniformSampler(rng, lower, range);
+        delegate = of(rng, lower, upper);
     }
 
     /** {@inheritDoc} */
@@ -179,4 +172,29 @@ public class DiscreteUniformSampler
         // Direct return of the optimised sampler
         return delegate.withUniformRandomProvider(rng);
     }
+
+    /**
+     * Creates a new discrete uniform distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param lower Lower bound (inclusive) of the distribution.
+     * @param upper Upper bound (inclusive) of the distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code lower > upper}.
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                int lower,
+                                                int upper) {
+        if (lower > upper) {
+            throw new IllegalArgumentException(lower  + " > " + upper);
+        }
+        // Choose the algorithm depending on the range
+        final int range = (upper - lower) + 1;
+        return range <= 0 ?
+            // The range is too wide to fit in a positive int (larger
+            // than 2^31); use a simple rejection method.
+            new LargeRangeDiscreteUniformSampler(rng, lower, upper) :
+            // Use a sample from the range added to the lower bound.
+            new SmallRangeDiscreteUniformSampler(rng, lower, range);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java
index 91c29e7..6b70b7c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java
@@ -88,4 +88,24 @@ public class GaussianSampler implements SharedStateContinuousSampler {
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new GaussianSampler(rng, this);
     }
+
+    /**
+     * Create a new normalised Gaussian sampler.
+     *
+     * <p>Note: The shared-state functionality is available if the {@link NormalizedGaussianSampler}
+     * is a {@link SharedStateSampler}. Otherwise a run-time exception will be thrown when the
+     * sampler is used to share state.</p>
+     *
+     * @param normalized Generator of N(0,1) Gaussian distributed random numbers.
+     * @param mean Mean of the Gaussian distribution.
+     * @param standardDeviation Standard deviation of the Gaussian distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code standardDeviation <= 0}
+     * @see #withUniformRandomProvider(UniformRandomProvider)
+     */
+    public static SharedStateContinuousSampler of(NormalizedGaussianSampler normalized,
+                                                  double mean,
+                                                  double standardDeviation) {
+        return new GaussianSampler(normalized, mean, standardDeviation);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
index 7cbca6b..a3f3339 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
@@ -100,7 +100,7 @@ public final class GeometricSampler {
             // is noted in the class Javadoc that the use of a small p leads to truncation so
             // no checks are made for this case.
             final double exponentialMean = 1.0 / (-Math.log1p(-probabilityOfSuccess));
-            exponentialSampler = new AhrensDieterExponentialSampler(rng, exponentialMean);
+            exponentialSampler = AhrensDieterExponentialSampler.of(rng, exponentialMean);
         }
 
         /**
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java
index 4401d9e..c0761d9 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java
@@ -96,4 +96,21 @@ public class InverseTransformContinuousSampler
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new InverseTransformContinuousSampler(rng, function);
     }
+
+    /**
+     * Create a new inverse-transform continuous sampler.
+     *
+     * <p>To use the sampler to
+     * {@link org.apache.commons.rng.sampling.SharedStateSampler share state} the function must be
+     * suitable for concurrent use.</p>
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param function Inverse cumulative probability function.
+     * @return the sampler
+     * @see #withUniformRandomProvider(UniformRandomProvider)
+     */
+    public static SharedStateContinuousSampler of(UniformRandomProvider rng,
+                                                  ContinuousInverseCumulativeProbabilityFunction function) {
+        return new InverseTransformContinuousSampler(rng, function);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java
index 6067a8c..da7ee63 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java
@@ -96,4 +96,21 @@ public class InverseTransformDiscreteSampler
     public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new InverseTransformDiscreteSampler(rng, function);
     }
+
+    /**
+     * Create a new inverse-transform discrete sampler.
+     *
+     * <p>To use the sampler to
+     * {@link org.apache.commons.rng.sampling.SharedStateSampler share state} the function must be
+     * suitable for concurrent use.</p>
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param function Inverse cumulative probability function.
+     * @return the sampler
+     * @see #withUniformRandomProvider(UniformRandomProvider)
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                DiscreteInverseCumulativeProbabilityFunction function) {
+        return new InverseTransformDiscreteSampler(rng, function);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java
index a7b6d7c..ff84cc6 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java
@@ -85,4 +85,19 @@ public class InverseTransformParetoSampler
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new InverseTransformParetoSampler(rng, this);
     }
+
+    /**
+     * Creates a new Pareto distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param scale Scale of the distribution.
+     * @param shape Shape of the distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code scale <= 0} or {@code shape <= 0}
+     */
+    public static SharedStateContinuousSampler of(UniformRandomProvider rng,
+                                                  double scale,
+                                                  double shape) {
+        return new InverseTransformParetoSampler(rng, scale, shape);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
index bc71e5b..72b3da5 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
@@ -132,8 +132,8 @@ public class LargeMeanPoissonSampler
         }
         this.rng = rng;
 
-        gaussian = new ZigguratNormalizedGaussianSampler(rng);
-        exponential = new AhrensDieterExponentialSampler(rng, 1);
+        gaussian = ZigguratNormalizedGaussianSampler.of(rng);
+        exponential = AhrensDieterExponentialSampler.of(rng, 1);
         // Plain constructor uses the uncached function.
         factorialLog = NO_CACHE_FACTORIAL_LOG;
 
@@ -177,8 +177,8 @@ public class LargeMeanPoissonSampler
         }
         this.rng = rng;
 
-        gaussian = new ZigguratNormalizedGaussianSampler(rng);
-        exponential = new AhrensDieterExponentialSampler(rng, 1);
+        gaussian = ZigguratNormalizedGaussianSampler.of(rng);
+        exponential = AhrensDieterExponentialSampler.of(rng, 1);
         // Plain constructor uses the uncached function.
         factorialLog = NO_CACHE_FACTORIAL_LOG;
 
@@ -307,6 +307,19 @@ public class LargeMeanPoissonSampler
     }
 
     /**
+     * Creates a new Poisson distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param mean Mean.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code mean < 1} or {@code mean > 0.5 *}
+     * {@link Integer#MAX_VALUE}.
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double mean) {
+        return new LargeMeanPoissonSampler(rng, mean);
+    }
+    /**
      * Gets the initialisation state of the sampler.
      *
      * <p>The state is computed using an integer {@code lambda} value of
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java
index 9bf9568..9fbfd90 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java
@@ -89,4 +89,24 @@ public class LogNormalSampler implements SharedStateContinuousSampler {
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new LogNormalSampler(rng, this);
     }
+
+    /**
+     * Create a new log-normal distribution sampler.
+     *
+     * <p>Note: The shared-state functionality is available if the {@link NormalizedGaussianSampler}
+     * is a {@link SharedStateSampler}. Otherwise a run-time exception will be thrown when the
+     * sampler is used to share state.</p>
+     *
+     * @param gaussian N(0,1) generator.
+     * @param scale Scale of the log-normal distribution.
+     * @param shape Shape of the log-normal distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code scale < 0} or {@code shape <= 0}.
+     * @see #withUniformRandomProvider(UniformRandomProvider)
+     */
+    public static SharedStateContinuousSampler of(NormalizedGaussianSampler gaussian,
+                                                  double scale,
+                                                  double shape) {
+        return new LogNormalSampler(gaussian, scale, shape);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java
index 7068052..516f67c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java
@@ -90,4 +90,17 @@ public class MarsagliaNormalizedGaussianSampler
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new MarsagliaNormalizedGaussianSampler(rng);
     }
+
+    /**
+     * Create a new normalised Gaussian sampler.
+     *
+     * @param <S> Sampler type.
+     * @param rng Generator of uniformly distributed random numbers.
+     * @return the sampler
+     */
+    @SuppressWarnings("unchecked")
+    public static <S extends NormalizedGaussianSampler & SharedStateContinuousSampler> S
+            of(UniformRandomProvider rng) {
+        return (S) new MarsagliaNormalizedGaussianSampler(rng);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
index 6834db9..7fe581c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
@@ -63,6 +63,9 @@ public class PoissonSampler
     private final SharedStateDiscreteSampler poissonSamplerDelegate;
 
     /**
+     * This instance delegates sampling. Use the factory method
+     * {@link #of(UniformRandomProvider, double)} to create an optimal sampler.
+     *
      * @param rng Generator of uniformly distributed random numbers.
      * @param mean Mean.
      * @throws IllegalArgumentException if {@code mean <= 0} or
@@ -73,10 +76,7 @@ public class PoissonSampler
         super(null);
 
         // Delegate all work to specialised samplers.
-        // These should check the input arguments.
-        poissonSamplerDelegate = mean < PIVOT ?
-            new SmallMeanPoissonSampler(rng, mean) :
-            new LargeMeanPoissonSampler(rng, mean);
+        poissonSamplerDelegate = of(rng, mean);
     }
 
     /** {@inheritDoc} */
@@ -97,4 +97,21 @@ public class PoissonSampler
         // Direct return of the optimised sampler
         return poissonSamplerDelegate.withUniformRandomProvider(rng);
     }
+
+    /**
+     * Creates a new Poisson distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param mean Mean.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean >}
+     * {@link Integer#MAX_VALUE}.
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double mean) {
+        // Each sampler should check the input arguments.
+        return mean < PIVOT ?
+            SmallMeanPoissonSampler.of(rng, mean) :
+            LargeMeanPoissonSampler.of(rng, mean);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCache.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCache.java
index 748b2ba..184f8d5 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCache.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCache.java
@@ -157,20 +157,20 @@ public class PoissonSamplerCache {
         // Ensure the same functionality as the PoissonSampler by
         // using a SmallMeanPoissonSampler under the switch point.
         if (mean < PoissonSampler.PIVOT) {
-            return new SmallMeanPoissonSampler(rng, mean);
+            return SmallMeanPoissonSampler.of(rng, mean);
         }
         if (mean > maxN) {
             // Outside the range of the cache.
             // This avoids extra parameter checks and handles the case when
             // the cache is empty or if Math.floor(mean) is not an integer.
-            return new LargeMeanPoissonSampler(rng, mean);
+            return LargeMeanPoissonSampler.of(rng, mean);
         }
 
         // Convert the mean into an integer.
         final int n = (int) Math.floor(mean);
         if (n < minN) {
             // Outside the lower range of the cache.
-            return new LargeMeanPoissonSampler(rng, mean);
+            return LargeMeanPoissonSampler.of(rng, mean);
         }
 
         // Look in the cache for a state that can be reused.
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java
index fb8cb9d..7459c7b 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java
@@ -199,6 +199,22 @@ public class RejectionInversionZipfSampler
     }
 
     /**
+     * Creates a new Zipf distribution sampler.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param numberOfElements Number of elements.
+     * @param exponent Exponent.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code numberOfElements <= 0} or
+     * {@code exponent <= 0}.
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                int numberOfElements,
+                                                double exponent) {
+        return new RejectionInversionZipfSampler(rng, numberOfElements, exponent);
+    }
+
+    /**
      * {@code H(x)} is defined as
      * <ul>
      *  <li>{@code (x^(1 - exponent) - 1) / (1 - exponent)}, if {@code exponent != 1}</li>
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java
index 331b6b0..d444003 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java
@@ -56,7 +56,7 @@ public class SmallMeanPoissonSampler
     /**
      * @param rng  Generator of uniformly distributed random numbers.
      * @param mean Mean.
-     * @throws IllegalArgumentException if {@code mean <= 0} or {@code Math.exp(-mean)} is not positive.
+     * @throws IllegalArgumentException if {@code mean <= 0} or {@code Math.exp(-mean) == 0}
      */
     public SmallMeanPoissonSampler(UniformRandomProvider rng,
                                    double mean) {
@@ -113,4 +113,17 @@ public class SmallMeanPoissonSampler
     public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new SmallMeanPoissonSampler(rng, this);
     }
+
+    /**
+     * Creates a new sampler for the Poisson distribution.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param mean Mean of the distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code mean <= 0} or {@code Math.exp(-mean) == 0}.
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double mean) {
+        return new SmallMeanPoissonSampler(rng, mean);
+    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java
index 6b8588a..96c3626 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java
@@ -166,4 +166,17 @@ public class ZigguratNormalizedGaussianSampler
     public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new ZigguratNormalizedGaussianSampler(rng);
     }
+
+    /**
+     * Create a new normalised Gaussian sampler.
+     *
+     * @param <S> Sampler type.
+     * @param rng Generator of uniformly distributed random numbers.
+     * @return the sampler
+     */
+    @SuppressWarnings("unchecked")
+    public static <S extends NormalizedGaussianSampler & SharedStateContinuousSampler> S
+            of(UniformRandomProvider rng) {
+        return (S) new ZigguratNormalizedGaussianSampler(rng);
+    }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java
index 6fe5139..e509d50 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java
@@ -34,9 +34,7 @@ public class AhrensDieterExponentialSamplerTest {
         final RestorableUniformRandomProvider rng =
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double mean = 0;
-        @SuppressWarnings("unused")
-        final AhrensDieterExponentialSampler sampler =
-            new AhrensDieterExponentialSampler(rng, mean);
+        AhrensDieterExponentialSampler.of(rng, mean);
     }
 
     /**
@@ -47,8 +45,8 @@ public class AhrensDieterExponentialSamplerTest {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double mean = 1.23;
-        final AhrensDieterExponentialSampler sampler1 =
-            new AhrensDieterExponentialSampler(rng1, mean);
+        final SharedStateContinuousSampler sampler1 =
+            AhrensDieterExponentialSampler.of(rng1, mean);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java
index 27a4198..f1290d0 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java
@@ -20,6 +20,7 @@ import org.apache.commons.rng.RestorableUniformRandomProvider;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.sampling.RandomAssert;
 import org.apache.commons.rng.simple.RandomSource;
+import org.junit.Assert;
 import org.junit.Test;
 
 /**
@@ -32,12 +33,10 @@ public class AhrensDieterMarsagliaTsangGammaSamplerTest {
     @Test(expected = IllegalArgumentException.class)
     public void testConstructorThrowsWithZeroAlpha() {
         final RestorableUniformRandomProvider rng =
-            RandomSource.create(RandomSource.SPLIT_MIX_64);
+            RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double alpha = 0;
         final double theta = 1;
-        @SuppressWarnings("unused")
-        final AhrensDieterMarsagliaTsangGammaSampler sampler =
-            new AhrensDieterMarsagliaTsangGammaSampler(rng, alpha, theta);
+        AhrensDieterMarsagliaTsangGammaSampler.of(rng, alpha, theta);
     }
 
     /**
@@ -46,12 +45,10 @@ public class AhrensDieterMarsagliaTsangGammaSamplerTest {
     @Test(expected = IllegalArgumentException.class)
     public void testConstructorThrowsWithZeroTheta() {
         final RestorableUniformRandomProvider rng =
-            RandomSource.create(RandomSource.SPLIT_MIX_64);
+            RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double alpha = 1;
         final double theta = 0;
-        @SuppressWarnings("unused")
-        final AhrensDieterMarsagliaTsangGammaSampler sampler =
-            new AhrensDieterMarsagliaTsangGammaSampler(rng, alpha, theta);
+        AhrensDieterMarsagliaTsangGammaSampler.of(rng, alpha, theta);
     }
 
     /**
@@ -79,9 +76,21 @@ public class AhrensDieterMarsagliaTsangGammaSamplerTest {
     private static void testSharedStateSampler(double alpha, double theta) {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
+        // Use instance constructor not factory constructor to exercise 1.X public API
         final AhrensDieterMarsagliaTsangGammaSampler sampler1 =
             new AhrensDieterMarsagliaTsangGammaSampler(rng1, alpha, theta);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
+
+    /**
+     * Test the toString method. This is added to ensure coverage as the factory constructor
+     * used in other tests does not create an instance of the wrapper class.
+     */
+    @Test
+    public void testToString() {
+        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
+        Assert.assertTrue(new AhrensDieterMarsagliaTsangGammaSampler(rng, 1.0, 2.0).toString()
+                .toLowerCase().contains("gamma"));
+    }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java
index 6e21e62..e928bec 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java
@@ -32,8 +32,8 @@ public class BoxMullerNormalisedGaussianSamplerTest {
     public void testSharedStateSampler() {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final BoxMullerNormalizedGaussianSampler sampler1 =
-            new BoxMullerNormalizedGaussianSampler(rng1);
+        final SharedStateContinuousSampler sampler1 =
+            BoxMullerNormalizedGaussianSampler.of(rng1);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java
index cb630aa..8325244 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java
@@ -35,9 +35,7 @@ public class ChengBetaSamplerTest {
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double alpha = 0;
         final double beta = 1;
-        @SuppressWarnings("unused")
-        final ChengBetaSampler sampler =
-            new ChengBetaSampler(rng, alpha, beta);
+        ChengBetaSampler.of(rng, alpha, beta);
     }
 
     /**
@@ -49,9 +47,7 @@ public class ChengBetaSamplerTest {
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double alpha = 1;
         final double beta = 0;
-        @SuppressWarnings("unused")
-        final ChengBetaSampler sampler =
-            new ChengBetaSampler(rng, alpha, beta);
+        ChengBetaSampler.of(rng, alpha, beta);
     }
 
     /**
@@ -63,8 +59,8 @@ public class ChengBetaSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double alpha = 1.23;
         final double beta = 4.56;
-        final ChengBetaSampler sampler1 =
-            new ChengBetaSampler(rng1, alpha, beta);
+        final SharedStateContinuousSampler sampler1 =
+            ChengBetaSampler.of(rng1, alpha, beta);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousSamplersList.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousSamplersList.java
index 2cd8325..f4ff9ae 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousSamplersList.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousSamplersList.java
@@ -52,16 +52,16 @@ public final class ContinuousSamplersList {
                 new BoxMullerGaussianSampler(RandomSource.create(RandomSource.MT), meanNormal, sigmaNormal));
             // Gaussian ("Box-Muller").
             add(LIST, new org.apache.commons.math3.distribution.NormalDistribution(unusedRng, meanNormal, sigmaNormal),
-                new GaussianSampler(new BoxMullerNormalizedGaussianSampler(RandomSource.create(RandomSource.MT)),
-                                    meanNormal, sigmaNormal));
+                GaussianSampler.of(BoxMullerNormalizedGaussianSampler.of(RandomSource.create(RandomSource.MT)),
+                                   meanNormal, sigmaNormal));
             // Gaussian ("Marsaglia").
             add(LIST, new org.apache.commons.math3.distribution.NormalDistribution(unusedRng, meanNormal, sigmaNormal),
-                new GaussianSampler(new MarsagliaNormalizedGaussianSampler(RandomSource.create(RandomSource.MT)),
-                                    meanNormal, sigmaNormal));
+                GaussianSampler.of(MarsagliaNormalizedGaussianSampler.of(RandomSource.create(RandomSource.MT)),
+                                   meanNormal, sigmaNormal));
             // Gaussian ("Ziggurat").
             add(LIST, new org.apache.commons.math3.distribution.NormalDistribution(unusedRng, meanNormal, sigmaNormal),
-                new GaussianSampler(new ZigguratNormalizedGaussianSampler(RandomSource.create(RandomSource.MT)),
-                                    meanNormal, sigmaNormal));
+                GaussianSampler.of(ZigguratNormalizedGaussianSampler.of(RandomSource.create(RandomSource.MT)),
+                                   meanNormal, sigmaNormal));
 
             // Beta ("inverse method").
             final double alphaBeta = 4.3;
@@ -70,16 +70,16 @@ public final class ContinuousSamplersList {
                 RandomSource.create(RandomSource.ISAAC));
             // Beta ("Cheng").
             add(LIST, new org.apache.commons.math3.distribution.BetaDistribution(unusedRng, alphaBeta, betaBeta),
-                new ChengBetaSampler(RandomSource.create(RandomSource.MWC_256), alphaBeta, betaBeta));
+                ChengBetaSampler.of(RandomSource.create(RandomSource.MWC_256), alphaBeta, betaBeta));
             add(LIST, new org.apache.commons.math3.distribution.BetaDistribution(unusedRng, betaBeta, alphaBeta),
-                new ChengBetaSampler(RandomSource.create(RandomSource.WELL_19937_A), betaBeta, alphaBeta));
+                ChengBetaSampler.of(RandomSource.create(RandomSource.WELL_19937_A), betaBeta, alphaBeta));
             // Beta ("Cheng", alternate algorithm).
             final double alphaBetaAlt = 0.5678;
             final double betaBetaAlt = 0.1234;
             add(LIST, new org.apache.commons.math3.distribution.BetaDistribution(unusedRng, alphaBetaAlt, betaBetaAlt),
-                new ChengBetaSampler(RandomSource.create(RandomSource.WELL_512_A), alphaBetaAlt, betaBetaAlt));
+                ChengBetaSampler.of(RandomSource.create(RandomSource.WELL_512_A), alphaBetaAlt, betaBetaAlt));
             add(LIST, new org.apache.commons.math3.distribution.BetaDistribution(unusedRng, betaBetaAlt, alphaBetaAlt),
-                new ChengBetaSampler(RandomSource.create(RandomSource.WELL_19937_C), betaBetaAlt, alphaBetaAlt));
+                ChengBetaSampler.of(RandomSource.create(RandomSource.WELL_19937_C), betaBetaAlt, alphaBetaAlt));
 
             // Cauchy ("inverse method").
             final double medianCauchy = 0.123;
@@ -98,7 +98,7 @@ public final class ContinuousSamplersList {
                 RandomSource.create(RandomSource.WELL_44497_A));
             // Exponential.
             add(LIST, new org.apache.commons.math3.distribution.ExponentialDistribution(unusedRng, meanExp),
-                new AhrensDieterExponentialSampler(RandomSource.create(RandomSource.MT), meanExp));
+                AhrensDieterExponentialSampler.of(RandomSource.create(RandomSource.MT), meanExp));
 
             // F ("inverse method").
             final int numDofF = 4;
@@ -114,11 +114,11 @@ public final class ContinuousSamplersList {
                 RandomSource.create(RandomSource.SPLIT_MIX_64));
             // Gamma (alpha < 1).
             add(LIST, new org.apache.commons.math3.distribution.GammaDistribution(unusedRng, alphaGammaSmallerThanOne, thetaGamma),
-                new AhrensDieterMarsagliaTsangGammaSampler(RandomSource.create(RandomSource.XOR_SHIFT_1024_S),
+                AhrensDieterMarsagliaTsangGammaSampler.of(RandomSource.create(RandomSource.XOR_SHIFT_1024_S),
                                                            alphaGammaSmallerThanOne, thetaGamma));
             // Gamma (alpha > 1).
             add(LIST, new org.apache.commons.math3.distribution.GammaDistribution(unusedRng, alphaGammaLargerThanOne, thetaGamma),
-                new AhrensDieterMarsagliaTsangGammaSampler(RandomSource.create(RandomSource.WELL_44497_B),
+                AhrensDieterMarsagliaTsangGammaSampler.of(RandomSource.create(RandomSource.WELL_44497_B),
                                                            alphaGammaLargerThanOne, thetaGamma));
 
             // Gumbel ("inverse method").
@@ -149,16 +149,16 @@ public final class ContinuousSamplersList {
                 new BoxMullerLogNormalSampler(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), scaleLogNormal, shapeLogNormal));
             // Log-normal ("Box-Muller").
             add(LIST, new org.apache.commons.math3.distribution.LogNormalDistribution(unusedRng, scaleLogNormal, shapeLogNormal),
-                new LogNormalSampler(new BoxMullerNormalizedGaussianSampler(RandomSource.create(RandomSource.XOR_SHIFT_1024_S)),
-                                     scaleLogNormal, shapeLogNormal));
+                LogNormalSampler.of(BoxMullerNormalizedGaussianSampler.of(RandomSource.create(RandomSource.XOR_SHIFT_1024_S)),
+                                    scaleLogNormal, shapeLogNormal));
             // Log-normal ("Marsaglia").
             add(LIST, new org.apache.commons.math3.distribution.LogNormalDistribution(unusedRng, scaleLogNormal, shapeLogNormal),
-                new LogNormalSampler(new MarsagliaNormalizedGaussianSampler(RandomSource.create(RandomSource.MT_64)),
-                                     scaleLogNormal, shapeLogNormal));
+                LogNormalSampler.of(MarsagliaNormalizedGaussianSampler.of(RandomSource.create(RandomSource.MT_64)),
+                                    scaleLogNormal, shapeLogNormal));
             // Log-normal ("Ziggurat").
             add(LIST, new org.apache.commons.math3.distribution.LogNormalDistribution(unusedRng, scaleLogNormal, shapeLogNormal),
-                new LogNormalSampler(new ZigguratNormalizedGaussianSampler(RandomSource.create(RandomSource.MWC_256)),
-                                     scaleLogNormal, shapeLogNormal));
+                LogNormalSampler.of(ZigguratNormalizedGaussianSampler.of(RandomSource.create(RandomSource.MWC_256)),
+                                    scaleLogNormal, shapeLogNormal));
 
             // Logistic ("inverse method").
             final double muLogistic = -123.456;
@@ -180,7 +180,7 @@ public final class ContinuousSamplersList {
                 RandomSource.create(RandomSource.TWO_CMRES_SELECT, null, 9, 11));
             // Pareto.
             add(LIST, new org.apache.commons.math3.distribution.ParetoDistribution(unusedRng, scalePareto, shapePareto),
-                new InverseTransformParetoSampler(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), scalePareto, shapePareto));
+                InverseTransformParetoSampler.of(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), scalePareto, shapePareto));
 
             // T ("inverse method").
             final double dofT = 0.76543;
@@ -201,7 +201,7 @@ public final class ContinuousSamplersList {
                 RandomSource.create(RandomSource.TWO_CMRES));
             // Uniform.
             add(LIST, new org.apache.commons.math3.distribution.UniformRealDistribution(unusedRng, loUniform, hiUniform),
-                new ContinuousUniformSampler(RandomSource.create(RandomSource.MT_64), loUniform, hiUniform));
+                ContinuousUniformSampler.of(RandomSource.create(RandomSource.MT_64), loUniform, hiUniform));
 
             // Weibull ("inverse method").
             final double alphaWeibull = 678.9;
@@ -231,7 +231,7 @@ public final class ContinuousSamplersList {
                             final org.apache.commons.math3.distribution.RealDistribution dist,
                             UniformRandomProvider rng) {
         final ContinuousSampler inverseMethodSampler =
-            new InverseTransformContinuousSampler(rng,
+            InverseTransformContinuousSampler.of(rng,
                 new ContinuousInverseCumulativeProbabilityFunction() {
                     @Override
                     public double inverseCumulativeProbability(double p) {
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
index 36b9eba..5e04f8b 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
@@ -40,7 +40,7 @@ public class ContinuousUniformSamplerTest {
 
     private static void testSampleInRange(UniformRandomProvider rng,
                                           double low, double high) {
-        ContinuousUniformSampler sampler = new ContinuousUniformSampler(rng, low, high);
+        final SharedStateContinuousSampler sampler = ContinuousUniformSampler.of(rng, low, high);
         final double min = Math.min(low,  high);
         final double max = Math.max(low,  high);
         for (int i = 0; i < 10; i++) {
@@ -58,8 +58,8 @@ public class ContinuousUniformSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double low = 1.23;
         final double high = 4.56;
-        final ContinuousUniformSampler sampler1 =
-            new ContinuousUniformSampler(rng1, low, high);
+        final SharedStateContinuousSampler sampler1 =
+            ContinuousUniformSampler.of(rng1, low, high);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
index 73c7cb7..b2be61e 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
@@ -94,14 +94,14 @@ public final class DiscreteSamplersList {
             // Uniform.
             add(LIST, new org.apache.commons.math3.distribution.UniformIntegerDistribution(unusedRng, loUniform, hiUniform),
                 MathArrays.sequence(8, -3, 1),
-                new DiscreteUniformSampler(RandomSource.create(RandomSource.MT_64), loUniform, hiUniform));
+                DiscreteUniformSampler.of(RandomSource.create(RandomSource.MT_64), loUniform, hiUniform));
             // Uniform (large range).
             final int halfMax = Integer.MAX_VALUE / 2;
             final int hiLargeUniform = halfMax + 10;
             final int loLargeUniform = -hiLargeUniform;
             add(LIST, new org.apache.commons.math3.distribution.UniformIntegerDistribution(unusedRng, loLargeUniform, hiLargeUniform),
                 MathArrays.sequence(20, -halfMax, halfMax / 10),
-                new DiscreteUniformSampler(RandomSource.create(RandomSource.WELL_1024_A), loLargeUniform, hiLargeUniform));
+                DiscreteUniformSampler.of(RandomSource.create(RandomSource.WELL_1024_A), loLargeUniform, hiLargeUniform));
 
             // Zipf ("inverse method").
             final int numElementsZipf = 5;
@@ -112,12 +112,12 @@ public final class DiscreteSamplersList {
             // Zipf.
             add(LIST, new org.apache.commons.math3.distribution.ZipfDistribution(unusedRng, numElementsZipf, exponentZipf),
                 MathArrays.sequence(5, 1, 1),
-                new RejectionInversionZipfSampler(RandomSource.create(RandomSource.WELL_19937_C), numElementsZipf, exponentZipf));
+                RejectionInversionZipfSampler.of(RandomSource.create(RandomSource.WELL_19937_C), numElementsZipf, exponentZipf));
             // Zipf (exponent close to 1).
             final double exponentCloseToOneZipf = 1 - 1e-10;
             add(LIST, new org.apache.commons.math3.distribution.ZipfDistribution(unusedRng, numElementsZipf, exponentCloseToOneZipf),
                 MathArrays.sequence(5, 1, 1),
-                new RejectionInversionZipfSampler(RandomSource.create(RandomSource.WELL_19937_C), numElementsZipf, exponentCloseToOneZipf));
+                RejectionInversionZipfSampler.of(RandomSource.create(RandomSource.WELL_19937_C), numElementsZipf, exponentCloseToOneZipf));
 
             // Poisson ("inverse method").
             final double epsilonPoisson = org.apache.commons.math3.distribution.PoissonDistribution.DEFAULT_EPSILON;
@@ -129,11 +129,11 @@ public final class DiscreteSamplersList {
             // Poisson.
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(10, 0, 1),
-                new PoissonSampler(RandomSource.create(RandomSource.KISS), meanPoisson));
+                PoissonSampler.of(RandomSource.create(RandomSource.KISS), meanPoisson));
             // Dedicated small mean poisson sampler
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(10, 0, 1),
-                new SmallMeanPoissonSampler(RandomSource.create(RandomSource.XO_SHI_RO_256_PLUS), meanPoisson));
+                SmallMeanPoissonSampler.of(RandomSource.create(RandomSource.XO_SHI_RO_256_PLUS), meanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(10, 0, 1),
                 KempSmallMeanPoissonSampler.of(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
@@ -144,11 +144,11 @@ public final class DiscreteSamplersList {
             final double largeMeanPoisson = 67.89;
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(50, (int) (largeMeanPoisson - 25), 1),
-                new PoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), largeMeanPoisson));
+                PoissonSampler.of(RandomSource.create(RandomSource.SPLIT_MIX_64), largeMeanPoisson));
             // Dedicated large mean poisson sampler
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(50, (int) (largeMeanPoisson - 25), 1),
-                new LargeMeanPoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), largeMeanPoisson));
+                LargeMeanPoissonSampler.of(RandomSource.create(RandomSource.SPLIT_MIX_64), largeMeanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(50, (int) (largeMeanPoisson - 25), 1),
                 MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PLUS), largeMeanPoisson));
@@ -156,11 +156,11 @@ public final class DiscreteSamplersList {
             final double veryLargeMeanPoisson = 543.21;
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(100, (int) (veryLargeMeanPoisson - 50), 1),
-                new PoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), veryLargeMeanPoisson));
+                PoissonSampler.of(RandomSource.create(RandomSource.SPLIT_MIX_64), veryLargeMeanPoisson));
             // Dedicated large mean poisson sampler
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(100, (int) (veryLargeMeanPoisson - 50), 1),
-                new LargeMeanPoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), veryLargeMeanPoisson));
+                LargeMeanPoissonSampler.of(RandomSource.create(RandomSource.SPLIT_MIX_64), veryLargeMeanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(100, (int) (veryLargeMeanPoisson - 50), 1),
                 MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_RO_SHI_RO_64_SS), veryLargeMeanPoisson));
@@ -198,7 +198,7 @@ public final class DiscreteSamplersList {
                             int[] points,
                             UniformRandomProvider rng) {
         final DiscreteSampler inverseMethodSampler =
-            new InverseTransformDiscreteSampler(rng,
+            InverseTransformDiscreteSampler.of(rng,
                 new DiscreteInverseCumulativeProbabilityFunction() {
                     @Override
                     public int inverseCumulativeProbability(double p) {
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
index 9b76e7a..0fd662a 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.rng.sampling.distribution;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.sampling.RandomAssert;
 import org.apache.commons.rng.simple.RandomSource;
+import org.junit.Assert;
 import org.junit.Test;
 
 /**
@@ -32,9 +33,8 @@ public class DiscreteUniformSamplerTest {
     public void testConstructorThrowsWithLowerAboveUpper() {
         final int upper = 55;
         final int lower = upper + 1;
-        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        @SuppressWarnings("unused")
-        DiscreteUniformSampler sampler = new DiscreteUniformSampler(rng, lower, upper);
+        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
+        DiscreteUniformSampler.of(rng, lower, upper);
     }
 
     /**
@@ -62,9 +62,20 @@ public class DiscreteUniformSamplerTest {
     private static void testSharedStateSampler(int lower, int upper) {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final DiscreteUniformSampler sampler1 =
+        // Use instance constructor not factory constructor to exercise 1.X public API
+        final SharedStateDiscreteSampler sampler1 =
             new DiscreteUniformSampler(rng1, lower, upper);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
+
+    /**
+     * Test the toString method. This is added to ensure coverage as the factory constructor
+     * used in other tests does not create an instance of the wrapper class.
+     */
+    @Test
+    public void testToString() {
+        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
+        Assert.assertTrue(new DiscreteUniformSampler(rng, 1, 2).toString().toLowerCase().contains("uniform"));
+    }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java
index 28ca3c0..cbf1606 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java
@@ -34,12 +34,10 @@ public class GaussianSamplerTest {
     public void testConstructorThrowsWithZeroStandardDeviation() {
         final RestorableUniformRandomProvider rng =
             RandomSource.create(RandomSource.SPLIT_MIX_64);
-        final NormalizedGaussianSampler gauss = new ZigguratNormalizedGaussianSampler(rng);
+        final NormalizedGaussianSampler gauss = ZigguratNormalizedGaussianSampler.of(rng);
         final double mean = 1;
         final double standardDeviation = 0;
-        @SuppressWarnings("unused")
-        final GaussianSampler sampler =
-            new GaussianSampler(gauss, mean, standardDeviation);
+        GaussianSampler.of(gauss, mean, standardDeviation);
     }
 
     /**
@@ -49,11 +47,11 @@ public class GaussianSamplerTest {
     public void testSharedStateSampler() {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final NormalizedGaussianSampler gauss = new ZigguratNormalizedGaussianSampler(rng1);
+        final NormalizedGaussianSampler gauss = ZigguratNormalizedGaussianSampler.of(rng1);
         final double mean = 1.23;
         final double standardDeviation = 4.56;
-        final GaussianSampler sampler1 =
-            new GaussianSampler(gauss, mean, standardDeviation);
+        final SharedStateContinuousSampler sampler1 =
+            GaussianSampler.of(gauss, mean, standardDeviation);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
@@ -73,8 +71,8 @@ public class GaussianSamplerTest {
         };
         final double mean = 1.23;
         final double standardDeviation = 4.56;
-        final GaussianSampler sampler1 =
-            new GaussianSampler(gauss, mean, standardDeviation);
+        final SharedStateContinuousSampler sampler1 =
+            GaussianSampler.of(gauss, mean, standardDeviation);
         sampler1.withUniformRandomProvider(rng2);
     }
 
@@ -88,8 +86,8 @@ public class GaussianSamplerTest {
         final NormalizedGaussianSampler gauss = new BadSharedStateNormalizedGaussianSampler();
         final double mean = 1.23;
         final double standardDeviation = 4.56;
-        final GaussianSampler sampler1 =
-            new GaussianSampler(gauss, mean, standardDeviation);
+        final SharedStateContinuousSampler sampler1 =
+            GaussianSampler.of(gauss, mean, standardDeviation);
         sampler1.withUniformRandomProvider(rng2);
     }
 
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java
index 99938cf..ad4b033 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java
@@ -39,8 +39,8 @@ public class InverseTransformContinuousSamplerTest {
         };
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final InverseTransformContinuousSampler sampler1 =
-            new InverseTransformContinuousSampler(rng1, function);
+        final SharedStateContinuousSampler sampler1 =
+            InverseTransformContinuousSampler.of(rng1, function);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java
index 778ffe5..1ed4475 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java
@@ -39,8 +39,8 @@ public class InverseTransformDiscreteSamplerTest {
         };
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final InverseTransformDiscreteSampler sampler1 =
-            new InverseTransformDiscreteSampler(rng1, function);
+        final SharedStateDiscreteSampler sampler1 =
+            InverseTransformDiscreteSampler.of(rng1, function);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java
index 3837d1d..22c3303 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java
@@ -35,9 +35,7 @@ public class InverseTransformParetoSamplerTest {
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double scale = 0;
         final double shape = 1;
-        @SuppressWarnings("unused")
-        final InverseTransformParetoSampler sampler =
-            new InverseTransformParetoSampler(rng, scale, shape);
+        InverseTransformParetoSampler.of(rng, scale, shape);
     }
 
     /**
@@ -49,9 +47,7 @@ public class InverseTransformParetoSamplerTest {
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double scale = 1;
         final double shape = 0;
-        @SuppressWarnings("unused")
-        final InverseTransformParetoSampler sampler =
-            new InverseTransformParetoSampler(rng, scale, shape);
+        InverseTransformParetoSampler.of(rng, scale, shape);
     }
 
     /**
@@ -63,8 +59,8 @@ public class InverseTransformParetoSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double scale = 1.23;
         final double shape = 4.56;
-        final InverseTransformParetoSampler sampler1 =
-            new InverseTransformParetoSampler(rng1, scale, shape);
+        final SharedStateContinuousSampler sampler1 =
+            InverseTransformParetoSampler.of(rng1, scale, shape);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java
index 9a8c674..034a8f1 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java
@@ -41,8 +41,7 @@ public class LargeMeanPoissonSamplerTest {
         final RestorableUniformRandomProvider rng =
                 RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double mean = Integer.MAX_VALUE / 2 + 1;
-        @SuppressWarnings("unused")
-        LargeMeanPoissonSampler sampler = new LargeMeanPoissonSampler(rng, mean);
+        LargeMeanPoissonSampler.of(rng, mean);
     }
 
     /**
@@ -53,8 +52,7 @@ public class LargeMeanPoissonSamplerTest {
         final RestorableUniformRandomProvider rng =
                 RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double mean = Math.nextAfter(1, -1);
-        @SuppressWarnings("unused")
-        LargeMeanPoissonSampler sampler = new LargeMeanPoissonSampler(rng, mean);
+        LargeMeanPoissonSampler.of(rng, mean);
     }
 
     /**
@@ -131,17 +129,15 @@ public class LargeMeanPoissonSamplerTest {
             final RestorableUniformRandomProvider rng1,
             final RestorableUniformRandomProvider rng2,
             double mean) {
-        final DiscreteSampler s1 = new LargeMeanPoissonSampler(rng1, mean);
+        final LargeMeanPoissonSampler s1 = new LargeMeanPoissonSampler(rng1, mean);
         final int n = (int) Math.floor(mean);
         final double lambdaFractional = mean - n;
-        final LargeMeanPoissonSamplerState state1 = ((LargeMeanPoissonSampler)s1).getState();
-        final DiscreteSampler s2 = new LargeMeanPoissonSampler(rng2, state1, lambdaFractional);
-        final LargeMeanPoissonSamplerState state2 = ((LargeMeanPoissonSampler)s2).getState();
+        final LargeMeanPoissonSamplerState state1 = s1.getState();
+        final LargeMeanPoissonSampler s2 = new LargeMeanPoissonSampler(rng2, state1, lambdaFractional);
+        final LargeMeanPoissonSamplerState state2 = s2.getState();
         Assert.assertEquals("State lambdas are not equal", state1.getLambda(), state2.getLambda());
         Assert.assertNotSame("States are the same object", state1, state2);
-        for (int j = 0; j < 10; j++) {
-            Assert.assertEquals("Not the same sample", s1.sample(), s2.sample());
-        }
+        RandomAssert.assertProduceSameSequence(s1, s2);
     }
 
     /**
@@ -169,8 +165,8 @@ public class LargeMeanPoissonSamplerTest {
     private static void testSharedStateSampler(double mean) {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final LargeMeanPoissonSampler sampler1 =
-            new LargeMeanPoissonSampler(rng1, mean);
+        final SharedStateDiscreteSampler sampler1 =
+            LargeMeanPoissonSampler.of(rng1, mean);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java
index 3da6447..4bc093d 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java
@@ -34,12 +34,10 @@ public class LogNormalSamplerTest {
     public void testConstructorThrowsWithNegativeScale() {
         final RestorableUniformRandomProvider rng =
             RandomSource.create(RandomSource.SPLIT_MIX_64);
-        final NormalizedGaussianSampler gauss = new ZigguratNormalizedGaussianSampler(rng);
+        final NormalizedGaussianSampler gauss = ZigguratNormalizedGaussianSampler.of(rng);
         final double scale = -1e-6;
         final double shape = 1;
-        @SuppressWarnings("unused")
-        final LogNormalSampler sampler =
-            new LogNormalSampler(gauss, scale, shape);
+        LogNormalSampler.of(gauss, scale, shape);
     }
 
     /**
@@ -49,12 +47,10 @@ public class LogNormalSamplerTest {
     public void testConstructorThrowsWithZeroShape() {
         final RestorableUniformRandomProvider rng =
             RandomSource.create(RandomSource.SPLIT_MIX_64);
-        final NormalizedGaussianSampler gauss = new ZigguratNormalizedGaussianSampler(rng);
+        final NormalizedGaussianSampler gauss = ZigguratNormalizedGaussianSampler.of(rng);
         final double scale = 1;
         final double shape = 0;
-        @SuppressWarnings("unused")
-        final LogNormalSampler sampler =
-            new LogNormalSampler(gauss, scale, shape);
+        LogNormalSampler.of(gauss, scale, shape);
     }
 
     /**
@@ -64,11 +60,11 @@ public class LogNormalSamplerTest {
     public void testSharedStateSampler() {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final NormalizedGaussianSampler gauss = new ZigguratNormalizedGaussianSampler(rng1);
+        final NormalizedGaussianSampler gauss = ZigguratNormalizedGaussianSampler.of(rng1);
         final double scale = 1.23;
         final double shape = 4.56;
-        final LogNormalSampler sampler1 =
-            new LogNormalSampler(gauss, scale, shape);
+        final SharedStateContinuousSampler sampler1 =
+            LogNormalSampler.of(gauss, scale, shape);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
@@ -88,8 +84,8 @@ public class LogNormalSamplerTest {
         };
         final double scale = 1.23;
         final double shape = 4.56;
-        final LogNormalSampler sampler1 =
-            new LogNormalSampler(gauss, scale, shape);
+        final SharedStateContinuousSampler sampler1 =
+            LogNormalSampler.of(gauss, scale, shape);
         sampler1.withUniformRandomProvider(rng2);
     }
 
@@ -103,8 +99,8 @@ public class LogNormalSamplerTest {
         final NormalizedGaussianSampler gauss = new BadSharedStateNormalizedGaussianSampler();
         final double scale = 1.23;
         final double shape = 4.56;
-        final LogNormalSampler sampler1 =
-            new LogNormalSampler(gauss, scale, shape);
+        final SharedStateContinuousSampler sampler1 =
+            LogNormalSampler.of(gauss, scale, shape);
         sampler1.withUniformRandomProvider(rng2);
     }
 
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java
index 1155ce4..ae58bd0 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java
@@ -32,8 +32,8 @@ public class MarsagliaNormalisedGaussianSamplerTest {
     public void testSharedStateSampler() {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final MarsagliaNormalizedGaussianSampler sampler1 =
-            new MarsagliaNormalizedGaussianSampler(rng1);
+        final SharedStateContinuousSampler sampler1 =
+            MarsagliaNormalizedGaussianSampler.of(rng1);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCacheTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCacheTest.java
index 2fe8a00..54c6fbf 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCacheTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerCacheTest.java
@@ -384,7 +384,7 @@ public class PoissonSamplerCacheTest {
             final RestorableUniformRandomProvider rng2,
             PoissonSamplerCache cache,
             double mean) {
-        final DiscreteSampler s1 = new PoissonSampler(rng1, mean);
+        final DiscreteSampler s1 = PoissonSampler.of(rng1, mean);
         final DiscreteSampler s2 = cache.createPoissonSampler(rng2, mean);
         for (int j = 0; j < 10; j++) {
             Assert.assertEquals(s1.sample(), s2.sample());
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java
index 362397d..4f433f3 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java
@@ -19,8 +19,10 @@ package org.apache.commons.rng.sampling.distribution;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.sampling.RandomAssert;
 import org.apache.commons.rng.simple.RandomSource;
+import org.junit.Assert;
 import org.junit.Test;
 
+
 /**
  * This test checks the {@link PoissonSampler} can be created
  * from a saved state.
@@ -50,9 +52,20 @@ public class PoissonSamplerTest {
     private static void testSharedStateSampler(double mean) {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final PoissonSampler sampler1 =
+        // Use instance constructor not factory constructor to exercise 1.X public API
+        final SharedStateDiscreteSampler sampler1 =
             new PoissonSampler(rng1, mean);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
+
+    /**
+     * Test the toString method. This is added to ensure coverage as the factory constructor
+     * used in other tests does not create an instance of the wrapper class.
+     */
+    @Test
+    public void testToString() {
+        final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
+        Assert.assertTrue(new PoissonSampler(rng, 1.23).toString().toLowerCase().contains("poisson"));
+    }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java
index 15621da..b75f70c 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java
@@ -35,9 +35,7 @@ public class RejectionInversionZipfSamplerTest {
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final int numberOfElements = 0;
         final double exponent = 1;
-        @SuppressWarnings("unused")
-        final RejectionInversionZipfSampler sampler =
-            new RejectionInversionZipfSampler(rng, numberOfElements, exponent);
+        RejectionInversionZipfSampler.of(rng, numberOfElements, exponent);
     }
 
     /**
@@ -49,9 +47,7 @@ public class RejectionInversionZipfSamplerTest {
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final int numberOfElements = 1;
         final double exponent = 0;
-        @SuppressWarnings("unused")
-        final RejectionInversionZipfSampler sampler =
-            new RejectionInversionZipfSampler(rng, numberOfElements, exponent);
+        RejectionInversionZipfSampler.of(rng, numberOfElements, exponent);
     }
 
     /**
@@ -63,8 +59,8 @@ public class RejectionInversionZipfSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final int numberOfElements = 7;
         final double exponent = 1.23;
-        final RejectionInversionZipfSampler sampler1 =
-            new RejectionInversionZipfSampler(rng1, numberOfElements, exponent);
+        final SharedStateDiscreteSampler sampler1 =
+            RejectionInversionZipfSampler.of(rng1, numberOfElements, exponent);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
index f21bf95..4b0f677 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
@@ -37,8 +37,7 @@ public class SmallMeanPoissonSamplerTest {
         // Note: p0 = Math.exp(-mean) => mean = -Math.log(p0).
         // Add to the limit on the mean to cause p0 to be zero.
         final double mean = -Math.log(p0) + 1;
-        @SuppressWarnings("unused")
-        SmallMeanPoissonSampler sampler = new SmallMeanPoissonSampler(rng, mean);
+        SmallMeanPoissonSampler.of(rng, mean);
     }
 
     /**
@@ -49,8 +48,7 @@ public class SmallMeanPoissonSamplerTest {
         final UniformRandomProvider rng =
             RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double mean = 0;
-        @SuppressWarnings("unused")
-        SmallMeanPoissonSampler sampler = new SmallMeanPoissonSampler(rng, mean);
+        SmallMeanPoissonSampler.of(rng, mean);
     }
 
     /**
@@ -73,7 +71,7 @@ public class SmallMeanPoissonSamplerTest {
             // CHECKSTYLE: resume all
         };
         for (double mean : new double[] {0.5, 1, 1.5, 2.2}) {
-            final SmallMeanPoissonSampler sampler = new SmallMeanPoissonSampler(rng, mean);
+            final SharedStateDiscreteSampler sampler = SmallMeanPoissonSampler.of(rng, mean);
             final int expected = (int) Math.ceil(1000 * mean);
             Assert.assertEquals(expected, sampler.sample());
         }
@@ -87,8 +85,8 @@ public class SmallMeanPoissonSamplerTest {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double mean = 1.23;
-        final SmallMeanPoissonSampler sampler1 =
-            new SmallMeanPoissonSampler(rng1, mean);
+        final SharedStateDiscreteSampler sampler1 =
+            SmallMeanPoissonSampler.of(rng1, mean);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
index 1772de4..a223ebe 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
@@ -45,7 +45,7 @@ public class ZigguratNormalizedGaussianSamplerTest {
             };
 
         // Infinite loop (in v1.1).
-        new ZigguratNormalizedGaussianSampler(bad).sample();
+        ZigguratNormalizedGaussianSampler.of(bad).sample();
     }
 
     /**
@@ -55,8 +55,8 @@ public class ZigguratNormalizedGaussianSamplerTest {
     public void testSharedStateSampler() {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final ZigguratNormalizedGaussianSampler sampler1 =
-            new ZigguratNormalizedGaussianSampler(rng1);
+        final SharedStateContinuousSampler sampler1 =
+            ZigguratNormalizedGaussianSampler.of(rng1);
         final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/ThreadLocalRandomSource.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/ThreadLocalRandomSource.java
index ee6054c..8a2c7dd 100644
--- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/ThreadLocalRandomSource.java
+++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/ThreadLocalRandomSource.java
@@ -37,7 +37,7 @@ import org.apache.commons.rng.UniformRandomProvider;
  *
  * // One-time Poisson sample
  * double mean = 12.3;
- * int counts = new PoissonSampler(rng, mean).sample();
+ * int counts = PoissonSampler.of(rng, mean).sample();
  * </code></pre>
  *
  * <p>Note if the {@link RandomSource} requires additional arguments then it is not
@@ -58,7 +58,7 @@ import org.apache.commons.rng.UniformRandomProvider;
  *
  * // One-time Poisson sample using a thread-safe random number generator
  * double mean = 12.3;
- * int counts = new PoissonSampler(rng.get(), mean).sample();
+ * int counts = PoissonSampler.of(rng.get(), mean).sample();
  * </code></pre>
  *
  * @since 1.3


[commons-rng] 05/08: RNG-110: Provide factory constructors for unreleased samplers.

Posted by ah...@apache.org.
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

commit b3a439bab6a71828b9d3426b37c895492de9a2ca
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jul 19 22:08:50 2019 +0100

    RNG-110: Provide factory constructors for unreleased samplers.
    
    These samplers have no requirement to maintain an instance constructor.
    The constructor has been made private.
---
 .../AliasMethodDiscreteSamplerPerformance.java     |    4 +-
 .../distribution/DiscreteSamplersPerformance.java  |   12 +-
 .../distribution/GeometricSamplersPerformance.java |    2 +-
 .../distribution/PoissonSamplersPerformance.java   |    2 +-
 .../distribution/AliasMethodDiscreteSampler.java   |   18 +-
 .../sampling/distribution/GeometricSampler.java    |   52 +-
 .../distribution/GuideTableDiscreteSampler.java    |  158 +--
 .../distribution/KempSmallMeanPoissonSampler.java  |   62 +-
 .../distribution/LargeMeanPoissonSampler.java      |    4 +-
 .../MarsagliaTsangWangDiscreteSampler.java         | 1063 ++++++++++----------
 .../AliasMethodDiscreteSamplerTest.java            |   14 +-
 .../distribution/DiscreteSamplersList.java         |   20 +-
 .../distribution/GeometricSamplerTest.java         |   18 +-
 .../GuideTableDiscreteSamplerTest.java             |   12 +-
 .../KempSmallMeanPoissonSamplerTest.java           |   16 +-
 .../MarsagliaTsangWangDiscreteSamplerTest.java     |   58 +-
 src/main/resources/pmd/pmd-ruleset.xml             |    3 +-
 17 files changed, 778 insertions(+), 740 deletions(-)

diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java
index 408e1ba..32d8307 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java
@@ -96,7 +96,7 @@ public class AliasMethodDiscreteSamplerPerformance {
         public void setup() {
             probabilities = createProbabilities(size);
             UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-            sampler = AliasMethodDiscreteSampler.create(rng, probabilities, alpha);
+            sampler = AliasMethodDiscreteSampler.of(rng, probabilities, alpha);
         }
 
         /**
@@ -155,6 +155,6 @@ public class AliasMethodDiscreteSamplerPerformance {
     @Benchmark
     public Object createSampler(DistributionData dist) {
         // For the construction the RNG can be null
-        return AliasMethodDiscreteSampler.create(null, dist.getProbabilities(), dist.getAlpha());
+        return AliasMethodDiscreteSampler.of(null, dist.getProbabilities(), dist.getAlpha());
     }
 }
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
index c8cb81d..03bae18 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
@@ -119,17 +119,17 @@ public class DiscreteSamplersPerformance {
                 // Note: Use with a fractional part to the mean includes a small mean sample
                 sampler = new LargeMeanPoissonSampler(rng, 41.7);
             } else if ("GeometricSampler".equals(samplerType)) {
-                sampler = new GeometricSampler(rng, 0.21);
+                sampler = GeometricSampler.of(rng, 0.21);
             } else if ("MarsagliaTsangWangDiscreteSampler".equals(samplerType)) {
-                sampler = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng, DISCRETE_PROBABILITIES);
+                sampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, DISCRETE_PROBABILITIES);
             } else if ("MarsagliaTsangWangPoissonSampler".equals(samplerType)) {
-                sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, 8.9);
+                sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, 8.9);
             } else if ("MarsagliaTsangWangBinomialSampler".equals(samplerType)) {
-                sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, 20, 0.33);
+                sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, 20, 0.33);
             } else if ("GuideTableDiscreteSampler".equals(samplerType)) {
-                sampler = new GuideTableDiscreteSampler(rng, DISCRETE_PROBABILITIES);
+                sampler = GuideTableDiscreteSampler.of(rng, DISCRETE_PROBABILITIES);
             } else if ("AliasMethodDiscreteSampler".equals(samplerType)) {
-                sampler = AliasMethodDiscreteSampler.create(rng, DISCRETE_PROBABILITIES);
+                sampler = AliasMethodDiscreteSampler.of(rng, DISCRETE_PROBABILITIES);
             }
         }
     }
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
index b87ef8f..7fc3e57 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
@@ -95,7 +95,7 @@ public class GeometricSamplersPerformance {
             final RandomSource randomSource = RandomSource.valueOf(randomSourceName);
             final UniformRandomProvider rng = RandomSource.create(randomSource);
             if ("GeometricSampler".equals(samplerType)) {
-                sampler = new GeometricSampler(rng, probabilityOfSuccess);
+                sampler = GeometricSampler.of(rng, probabilityOfSuccess);
             } else {
                 final DiscreteInverseCumulativeProbabilityFunction geometricFunction =
                     new GeometricDiscreteInverseCumulativeProbabilityFunction(probabilityOfSuccess);
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
index fbce71e..2ef6f8b 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
@@ -185,7 +185,7 @@ public class PoissonSamplersPerformance {
                 factory = new DiscreteSamplerFactory() {
                     @Override
                     public DiscreteSampler create() {
-                        return new KempSmallMeanPoissonSampler(generator, mean);
+                        return KempSmallMeanPoissonSampler.of(generator, mean);
                     }
                 };
             } else if ("BoundedKempSmallMeanPoissonSampler".equals(samplerType)) {
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
index 2ad3593..2fa1ef7 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
@@ -52,7 +52,7 @@ import java.util.Arrays;
  * approximately 12 bytes of storage per input probability, that is {@code n * 12} for size
  * {@code n}. Zero-padding only requires 4 bytes of storage per padded value as the probability is
  * known to be zero. A table can be padded to a power of 2 using the utility function
- * {@link #create(UniformRandomProvider, double[], int)} to construct the sampler.</p>
+ * {@link #of(UniformRandomProvider, double[], int)} to construct the sampler.</p>
  *
  * <p>An optimisation is performed for small table sizes that are a power of 2. In this case the
  * sampling uses 1 or 2 calls from {@link UniformRandomProvider#nextInt()} to generate up to
@@ -293,7 +293,7 @@ public class AliasMethodDiscreteSampler
      * power-of-two. Padding is bounded by the upper limit on the size of an array.</p>
      *
      * <p>To avoid zero-padding use the
-     * {@link #create(UniformRandomProvider, double[], int)} method with a negative
+     * {@link #of(UniformRandomProvider, double[], int)} method with a negative
      * {@code alpha} factor.</p>
      *
      * @param rng Generator of uniformly distributed random numbers.
@@ -302,11 +302,11 @@ public class AliasMethodDiscreteSampler
      * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
      * probability is negative, infinite or {@code NaN}, or the sum of all
      * probabilities is not strictly positive.
-     * @see #create(UniformRandomProvider, double[], int)
+     * @see #of(UniformRandomProvider, double[], int)
      */
-    public static AliasMethodDiscreteSampler create(final UniformRandomProvider rng,
-                                                    final double[] probabilities) {
-        return create(rng, probabilities, DEFAULT_ALPHA);
+    public static SharedStateDiscreteSampler of(final UniformRandomProvider rng,
+                                                final double[] probabilities) {
+        return of(rng, probabilities, DEFAULT_ALPHA);
     }
 
     /**
@@ -348,9 +348,9 @@ public class AliasMethodDiscreteSampler
      * probability is negative, infinite or {@code NaN}, or the sum of all
      * probabilities is not strictly positive.
      */
-    public static AliasMethodDiscreteSampler create(final UniformRandomProvider rng,
-                                                    final double[] probabilities,
-                                                    int alpha) {
+    public static SharedStateDiscreteSampler of(final UniformRandomProvider rng,
+                                                final double[] probabilities,
+                                                int alpha) {
         // The Alias method balances N categories with counts around the mean into N sections,
         // each allocated 'mean' observations.
         //
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
index 25a4703..7cbca6b 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
@@ -45,10 +45,7 @@ import org.apache.commons.rng.UniformRandomProvider;
  *
  * @since 1.3
  */
-public class GeometricSampler implements SharedStateDiscreteSampler {
-    /** The appropriate geometric sampler for the parameters. */
-    private final SharedStateDiscreteSampler delegate;
-
+public final class GeometricSampler {
     /**
      * Sample from the geometric distribution when the probability of success is 1.
      */
@@ -132,48 +129,29 @@ public class GeometricSampler implements SharedStateDiscreteSampler {
         }
     }
 
+    /** Class contains only static methods. */
+    private GeometricSampler() {}
+
     /**
-     * Creates a new geometric distribution sampler. The samples will be provided in the set
-     * {@code k=[0, 1, 2, ...]} where {@code k} indicates the number of failures before the first
-     * success.
+     * Creates a new geometric distribution sampler. The samples will be provided in
+     * the set {@code k=[0, 1, 2, ...]} where {@code k} indicates the number of
+     * failures before the first success.
      *
-     * @param rng Generator of uniformly distributed random numbers
-     * @param probabilityOfSuccess The probability of success
-     * @throws IllegalArgumentException if {@code probabilityOfSuccess} is not in the range
-     * {@code [0 < probabilityOfSuccess <= 1]})
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param probabilityOfSuccess The probability of success.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code probabilityOfSuccess} is not in
+     * the range {@code [0 < probabilityOfSuccess <= 1]})
      */
-    public GeometricSampler(UniformRandomProvider rng, double probabilityOfSuccess) {
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double probabilityOfSuccess) {
         if (probabilityOfSuccess <= 0 || probabilityOfSuccess > 1) {
             throw new IllegalArgumentException(
                 "Probability of success (p) must be in the range [0 < p <= 1]: " +
                     probabilityOfSuccess);
         }
-        delegate = probabilityOfSuccess == 1 ?
+        return probabilityOfSuccess == 1 ?
             GeometricP1Sampler.INSTANCE :
             new GeometricExponentialSampler(rng, probabilityOfSuccess);
     }
-
-    /**
-     * Create a sample from a geometric distribution.
-     *
-     * <p>The sample will take the values in the set {@code [0, 1, 2, ...]}, equivalent to the
-     * number of failures before the first success.
-     */
-    @Override
-    public int sample() {
-        return delegate.sample();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return delegate.toString();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        // Direct return of the optimised sampler
-        return delegate.withUniformRandomProvider(rng);
-    }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
index 0ca3315..3ad4218 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
@@ -19,27 +19,27 @@ package org.apache.commons.rng.sampling.distribution;
 import org.apache.commons.rng.UniformRandomProvider;
 
 /**
- * Compute a sample from a discrete probability distribution. The cumulative probability
- * distribution is searched using a guide table to set an initial start point. This implementation
- * is based on:
+ * Compute a sample from {@code n} values each with an associated probability. If all unique items
+ * are assigned the same probability it is more efficient to use the {@link DiscreteUniformSampler}.
  *
- * <ul>
- *  <li>
- *   <blockquote>
- *    Devroye, Luc (1986). Non-Uniform Random Variate Generation.
- *    New York: Springer-Verlag. Chapter 3.2.4 "The method of guide tables" p. 96.
- *   </blockquote>
- *  </li>
- * </ul>
+ * <p>The cumulative probability distribution is searched using a guide table to set an
+ * initial start point. This implementation is based on:</p>
+ *
+ * <blockquote>
+ *  Devroye, Luc (1986). Non-Uniform Random Variate Generation.
+ *  New York: Springer-Verlag. Chapter 3.2.4 "The method of guide tables" p. 96.
+ * </blockquote>
  *
  * <p>The size of the guide table can be controlled using a parameter. A larger guide table
  * will improve performance at the cost of storage space.</p>
  *
  * <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p>
  *
+ * @see <a href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution">
+ * Discrete probability distribution (Wikipedia)</a>
  * @since 1.3
  */
-public class GuideTableDiscreteSampler
+public final class GuideTableDiscreteSampler
     implements SharedStateDiscreteSampler {
     /** The default value for {@code alpha}. */
     private static final double DEFAULT_ALPHA = 1.0;
@@ -63,38 +63,93 @@ public class GuideTableDiscreteSampler
     private final int[] guideTable;
 
     /**
-     * Create a new instance using the default guide table size.
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param cumulativeProbabilities The cumulative probability table ({@code f(x)}).
+     * @param guideTable The inverse cumulative probability guide table.
+     */
+    private GuideTableDiscreteSampler(UniformRandomProvider rng,
+                                      double[] cumulativeProbabilities,
+                                      int[] guideTable) {
+        this.rng = rng;
+        this.cumulativeProbabilities = cumulativeProbabilities;
+        this.guideTable = guideTable;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int sample() {
+        // Compute a probability
+        final double u = rng.nextDouble();
+
+        // Initialise the search using the guide table to find an initial guess.
+        // The table provides an upper bound on the sample (x+1) for a known
+        // cumulative probability (f(x)).
+        int x = guideTable[getGuideTableIndex(u, guideTable.length)];
+        // Search down.
+        // In the edge case where u is 1.0 then 'x' will be 1 outside the range of the
+        // cumulative probability table and this will decrement to a valid range.
+        // In the case where 'u' is mapped to the same guide table index as a lower
+        // cumulative probability f(x) (due to rounding down) then this will not decrement
+        // and return the exclusive upper bound (x+1).
+        while (x != 0 && u <= cumulativeProbabilities[x - 1]) {
+            x--;
+        }
+        return x;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "Guide table deviate [" + rng.toString() + "]";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        return new GuideTableDiscreteSampler(rng, cumulativeProbabilities, guideTable);
+    }
+
+    /**
+     * Create a new sampler for an enumerated distribution using the given {@code probabilities}.
+     * The samples corresponding to each probability are assumed to be a natural sequence
+     * starting at zero.
+     *
+     * <p>The size of the guide table is {@code probabilities.length}.</p>
      *
      * @param rng Generator of uniformly distributed random numbers.
      * @param probabilities The probabilities.
+     * @return the sampler
      * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
      * probability is negative, infinite or {@code NaN}, or the sum of all
      * probabilities is not strictly positive.
      */
-    public GuideTableDiscreteSampler(UniformRandomProvider rng,
-                                     double[] probabilities) {
-        this(rng, probabilities, DEFAULT_ALPHA);
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double[] probabilities) {
+        return of(rng, probabilities, DEFAULT_ALPHA);
     }
 
     /**
-     * Create a new instance.
+     * Create a new sampler for an enumerated distribution using the given {@code probabilities}.
+     * The samples corresponding to each probability are assumed to be a natural sequence
+     * starting at zero.
      *
-     * <p>The size of the guide table is {@code alpha * probabilities.length}.
+     * <p>The size of the guide table is {@code alpha * probabilities.length}.</p>
      *
      * @param rng Generator of uniformly distributed random numbers.
      * @param probabilities The probabilities.
      * @param alpha The alpha factor used to set the guide table size.
+     * @return the sampler
      * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
      * probability is negative, infinite or {@code NaN}, the sum of all
      * probabilities is not strictly positive, or {@code alpha} is not strictly positive.
      */
-    public GuideTableDiscreteSampler(UniformRandomProvider rng,
-                                     double[] probabilities,
-                                     double alpha) {
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double[] probabilities,
+                                                double alpha) {
         validateParameters(probabilities, alpha);
 
         final int size = probabilities.length;
-        cumulativeProbabilities = new double[size];
+        final double[] cumulativeProbabilities = new double[size];
 
         double sumProb = 0;
         int count = 0;
@@ -110,12 +165,10 @@ public class GuideTableDiscreteSampler
             throw new IllegalArgumentException("Invalid sum of probabilities: " + sumProb);
         }
 
-        this.rng = rng;
-
         // Note: The guide table is at least length 1. Compute the size avoiding overflow
         // in case (alpha * size) is too large.
         final int guideTableSize = (int) Math.ceil(alpha * size);
-        guideTable = new int[Math.max(guideTableSize, guideTableSize + 1)];
+        final int[] guideTable = new int[Math.max(guideTableSize, guideTableSize + 1)];
 
         // Compute and store cumulative probability.
         for (int x = 0; x < size; x++) {
@@ -123,7 +176,8 @@ public class GuideTableDiscreteSampler
             cumulativeProbabilities[x] = (norm < 1) ? norm : 1.0;
 
             // Set the guide table value as an exclusive upper bound (x + 1)
-            guideTable[getGuideTableIndex(cumulativeProbabilities[x])] = x + 1;
+            final int index = getGuideTableIndex(cumulativeProbabilities[x], guideTable.length);
+            guideTable[index] = x + 1;
         }
 
         // Edge case for round-off
@@ -139,17 +193,8 @@ public class GuideTableDiscreteSampler
         for (int i = 1; i < guideTable.length; i++) {
             guideTable[i] = Math.max(guideTable[i - 1], guideTable[i]);
         }
-    }
 
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param source Source to copy.
-     */
-    private GuideTableDiscreteSampler(UniformRandomProvider rng,
-                                      GuideTableDiscreteSampler source) {
-        this.rng = rng;
-        cumulativeProbabilities = source.cumulativeProbabilities;
-        guideTable = source.guideTable;
+        return new GuideTableDiscreteSampler(rng, cumulativeProbabilities, guideTable);
     }
 
     /**
@@ -171,48 +216,15 @@ public class GuideTableDiscreteSampler
 
     /**
      * Gets the guide table index for the probability. This is obtained using
-     * {@code p * (guideTable.length - 1)} so is inside the length of the table.
+     * {@code p * (tableLength - 1)} so is inside the length of the table.
      *
      * @param p Cumulative probability.
+     * @param tableLength Table length.
      * @return the guide table index.
      */
-    private int getGuideTableIndex(double p) {
+    private static int getGuideTableIndex(double p, int tableLength) {
         // Note: This is only ever called when p is in the range of the cumulative
         // probability table. So assume 0 <= p <= 1.
-        return (int) (p * (guideTable.length - 1));
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int sample() {
-        // Compute a probability
-        final double u = rng.nextDouble();
-
-        // Initialise the search using the guide table to find an initial guess.
-        // The table provides an upper bound on the sample (x+1) for a known
-        // cumulative probability (f(x)).
-        int x = guideTable[getGuideTableIndex(u)];
-        // Search down.
-        // In the edge case where u is 1.0 then 'x' will be 1 outside the range of the
-        // cumulative probability table and this will decrement to a valid range.
-        // In the case where 'u' is mapped to the same guide table index as a lower
-        // cumulative probability f(x) (due to rounding down) then this will not decrement
-        // and return the exclusive upper bound (x+1).
-        while (x != 0 && u <= cumulativeProbabilities[x - 1]) {
-            x--;
-        }
-        return x;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return "Guide table deviate [" + rng.toString() + "]";
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        return new GuideTableDiscreteSampler(rng, this);
+        return (int) (p * (tableLength - 1));
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
index 7c4a09c..353651d 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
@@ -41,11 +41,11 @@ import org.apache.commons.rng.UniformRandomProvider;
  * <p>Sampling uses 1 call to {@link UniformRandomProvider#nextDouble()}. This method provides
  * an alternative to the {@link SmallMeanPoissonSampler} for slow generators of {@code double}.</p>
  *
- * @since 1.3
  * @see <a href="https://www.jstor.org/stable/2346348">Kemp, A.W. (1981) JRSS Vol. 30, pp.
  * 249-253</a>
+ * @since 1.3
  */
-public class KempSmallMeanPoissonSampler
+public final class KempSmallMeanPoissonSampler
     implements SharedStateDiscreteSampler {
     /** Underlying source of randomness. */
     private final UniformRandomProvider rng;
@@ -61,35 +61,15 @@ public class KempSmallMeanPoissonSampler
 
     /**
      * @param rng Generator of uniformly distributed random numbers.
+     * @param p0 Probability of the Poisson sample {@code p(x=0)}.
      * @param mean Mean.
-     * @throws IllegalArgumentException if {@code mean <= 0} or
-     * {@code Math.exp(-mean) == 0}.
-     */
-    public KempSmallMeanPoissonSampler(UniformRandomProvider rng,
-                                       double mean) {
-        if (mean <= 0) {
-            throw new IllegalArgumentException("Mean is not strictly positive: " + mean);
-        }
-
-        p0 = Math.exp(-mean);
-        if (p0 > 0) {
-            this.rng = rng;
-            this.mean = mean;
-        } else {
-            // This catches the edge case of a NaN mean
-            throw new IllegalArgumentException("No probability for mean " + mean);
-        }
-    }
-
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param source Source to copy.
      */
     private KempSmallMeanPoissonSampler(UniformRandomProvider rng,
-                                        KempSmallMeanPoissonSampler source) {
+                                        double p0,
+                                        double mean) {
         this.rng = rng;
-        p0 = source.p0;
-        mean = source.mean;
+        this.p0 = p0;
+        this.mean = mean;
     }
 
     /** {@inheritDoc} */
@@ -129,6 +109,32 @@ public class KempSmallMeanPoissonSampler
     /** {@inheritDoc} */
     @Override
     public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-        return new KempSmallMeanPoissonSampler(rng, this);
+        return new KempSmallMeanPoissonSampler(rng, p0, mean);
+    }
+
+    /**
+     * Creates a new sampler for the Poisson distribution.
+     *
+     * @param rng Generator of uniformly distributed random numbers.
+     * @param mean Mean of the distribution.
+     * @return the sampler
+     * @throws IllegalArgumentException if {@code mean <= 0} or
+     * {@code Math.exp(-mean) == 0}.
+     */
+    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                double mean) {
+        if (mean <= 0) {
+            throw new IllegalArgumentException("Mean is not strictly positive: " + mean);
+        }
+
+        final double p0 = Math.exp(-mean);
+
+        // Probability must be positive. As mean increases then p(0) decreases.
+        if (p0 > 0) {
+            return new KempSmallMeanPoissonSampler(rng, p0, mean);
+        }
+
+        // This catches the edge case of a NaN mean
+        throw new IllegalArgumentException("No probability for mean: " + mean);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
index 2a68e32..bc71e5b 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
@@ -155,7 +155,7 @@ public class LargeMeanPoissonSampler
         final double lambdaFractional = mean - lambda;
         smallMeanPoissonSampler = (lambdaFractional < Double.MIN_VALUE) ?
             NO_SMALL_MEAN_POISSON_SAMPLER : // Not used.
-            new KempSmallMeanPoissonSampler(rng, lambdaFractional);
+            KempSmallMeanPoissonSampler.of(rng, lambdaFractional);
     }
 
     /**
@@ -196,7 +196,7 @@ public class LargeMeanPoissonSampler
         // The algorithm requires a Poisson sample from the remaining lambda fraction.
         smallMeanPoissonSampler = (lambdaFractional < Double.MIN_VALUE) ?
             NO_SMALL_MEAN_POISSON_SAMPLER : // Not used.
-            new KempSmallMeanPoissonSampler(rng, lambdaFractional);
+            KempSmallMeanPoissonSampler.of(rng, lambdaFractional);
     }
 
     /**
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
index 32bc12e..97b9228 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
@@ -40,17 +40,16 @@ import org.apache.commons.rng.UniformRandomProvider;
  * <p>The sampler supports the following distributions:</p>
  *
  * <ul>
- *  <li>Any discrete probability distribution (probabilities must be provided)
+ *  <li>Enumerated distribution (probabilities must be provided for each sample)
  *  <li>Poisson distribution up to {@code mean = 1024}
  *  <li>Binomial distribution up to {@code trials = 65535}
  * </ul>
  *
- * @since 1.3
  * @see <a href="http://dx.doi.org/10.18637/jss.v011.i03">Margsglia, et al (2004) JSS Vol.
  * 11, Issue 3</a>
+ * @since 1.3
  */
-public abstract class MarsagliaTsangWangDiscreteSampler
-    implements SharedStateDiscreteSampler {
+public final class MarsagliaTsangWangDiscreteSampler {
     /** The value 2<sup>8</sup> as an {@code int}. */
     private static final int INT_8 = 1 << 8;
     /** The value 2<sup>16</sup> as an {@code int}. */
@@ -60,34 +59,6 @@ public abstract class MarsagliaTsangWangDiscreteSampler
     /** The value 2<sup>31</sup> as a {@code double}. */
     private static final double DOUBLE_31 = 1L << 31;
 
-    /** The general name of any discrete probability distribution. */
-    private static final String DISCRETE_NAME = "discrete";
-    /** The name of the Poisson distribution. */
-    private static final String POISSON_NAME = "Poisson";
-    /** The name of the Binomial distribution. */
-    private static final String BINOMIAL_NAME = "Binomial";
-
-    /**
-     * Upper bound on the mean for the Poisson distribution.
-     *
-     * <p>The original source code provided in Marsaglia, et al (2004) has no explicit
-     * limit but the code fails at mean >= 1941 as the transform to compute p(x=mode)
-     * produces infinity. Use a conservative limit of 1024.</p>
-     */
-    private static final double MAX_POISSON_MEAN = 1024;
-    /**
-     * The threshold for the mean of the Poisson distribution to switch the method used
-     * to compute the probabilities. This is taken from the example software provided by
-     * Marsaglia, et al (2004).
-     */
-    private static final double POISSON_MEAN_THRESHOLD = 21.4;
-
-    /** Underlying source of randomness. */
-    protected final UniformRandomProvider rng;
-
-    /** The name of the distribution. */
-    private final String distributionName;
-
     // =========================================================================
     // Implementation note:
     //
@@ -109,15 +80,55 @@ public abstract class MarsagliaTsangWangDiscreteSampler
     // when provided via an array of probabilities and the Poisson and Binomial
     // distributions for a restricted set of parameters. The restrictions are
     // imposed by the requirement to compute the entire probability distribution
-    // from the controlling parameter(s) using a recursive method.
+    // from the controlling parameter(s) using a recursive method. Factory
+    // constructors return a SharedStateDiscreteSampler instance. Each distribution
+    // type is contained in an inner class.
     // =========================================================================
 
     /**
+     * The base class for Marsaglia-Tsang-Wang samplers.
+     */
+    private abstract static class AbstractMarsagliaTsangWangDiscreteSampler
+            implements SharedStateDiscreteSampler {
+        /** Underlying source of randomness. */
+        protected final UniformRandomProvider rng;
+
+        /** The name of the distribution. */
+        private final String distributionName;
+
+        /**
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param distributionName Distribution name.
+         */
+        AbstractMarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
+                                                  String distributionName) {
+            this.rng = rng;
+            this.distributionName = distributionName;
+        }
+
+        /**
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param source Source to copy.
+         */
+        AbstractMarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
+                                                  AbstractMarsagliaTsangWangDiscreteSampler source) {
+            this.rng = rng;
+            this.distributionName = source.distributionName;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public String toString() {
+            return "Marsaglia Tsang Wang " + distributionName + " deviate [" + rng.toString() + "]";
+        }
+    }
+
+    /**
      * An implementation for the sample algorithm based on the decomposition of the
      * index in the range {@code [0,2^30)} into 5 base-64 digits with 8-bit backing storage.
      */
     private static class MarsagliaTsangWangBase64Int8DiscreteSampler
-        extends MarsagliaTsangWangDiscreteSampler {
+        extends AbstractMarsagliaTsangWangDiscreteSampler {
         /** The mask to convert a {@code byte} to an unsigned 8-bit integer. */
         private static final int MASK = 0xff;
 
@@ -257,7 +268,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
      * index in the range {@code [0,2^30)} into 5 base-64 digits with 16-bit backing storage.
      */
     private static class MarsagliaTsangWangBase64Int16DiscreteSampler
-        extends MarsagliaTsangWangDiscreteSampler {
+        extends AbstractMarsagliaTsangWangDiscreteSampler {
         /** The mask to convert a {@code byte} to an unsigned 16-bit integer. */
         private static final int MASK = 0xffff;
 
@@ -397,7 +408,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
      * index in the range {@code [0,2^30)} into 5 base-64 digits with 32-bit backing storage.
      */
     private static class MarsagliaTsangWangBase64Int32DiscreteSampler
-        extends MarsagliaTsangWangDiscreteSampler {
+        extends AbstractMarsagliaTsangWangDiscreteSampler {
         /** Limit for look-up table 1. */
         private final int t1;
         /** Limit for look-up table 2. */
@@ -528,108 +539,10 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         }
     }
 
-    /**
-     * Return a fixed result for the Binomial distribution. This is a special class to handle
-     * an edge case of probability of success equal to 0 or 1.
-     */
-    private static class MarsagliaTsangWangFixedResultBinomialSampler
-        extends MarsagliaTsangWangDiscreteSampler {
-        /** The result. */
-        private final int result;
-
-        /**
-         * @param result Result.
-         */
-        MarsagliaTsangWangFixedResultBinomialSampler(int result) {
-            super(null, BINOMIAL_NAME);
-            this.result = result;
-        }
-
-        @Override
-        public int sample() {
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return BINOMIAL_NAME + " deviate";
-        }
-
-        @Override
-        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-            // No shared state
-            return this;
-        }
-    }
-
-    /**
-     * Return an inversion result for the Binomial distribution. This assumes the
-     * following:
-     *
-     * <pre>
-     * Binomial(n, p) = 1 - Binomial(n, 1 - p)
-     * </pre>
-     */
-    private static class MarsagliaTsangWangInversionBinomialSampler
-        extends MarsagliaTsangWangDiscreteSampler {
-        /** The number of trials. */
-        private final int trials;
-        /** The Binomial distribution sampler. */
-        private final SharedStateDiscreteSampler sampler;
-
-        /**
-         * @param trials Number of trials.
-         * @param sampler Binomial distribution sampler.
-         */
-        MarsagliaTsangWangInversionBinomialSampler(int trials,
-                                                   SharedStateDiscreteSampler sampler) {
-            super(null, BINOMIAL_NAME);
-            this.trials = trials;
-            this.sampler = sampler;
-        }
-
-        @Override
-        public int sample() {
-            return trials - sampler.sample();
-        }
-
-        @Override
-        public String toString() {
-            return sampler.toString();
-        }
-
-        @Override
-        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
-            return new MarsagliaTsangWangInversionBinomialSampler(this.trials,
-                this.sampler.withUniformRandomProvider(rng));
-        }
-    }
-
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param distributionName Distribution name.
-     */
-    MarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
-                                      String distributionName) {
-        this.rng = rng;
-        this.distributionName = distributionName;
-    }
 
-    /**
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param source Source to copy.
-     */
-    MarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
-                                      MarsagliaTsangWangDiscreteSampler source) {
-        this.rng = rng;
-        this.distributionName = source.distributionName;
-    }
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return "Marsaglia Tsang Wang " + distributionName + " deviate [" + rng.toString() + "]";
-    }
+    /** Class contains only static methods. */
+    private MarsagliaTsangWangDiscreteSampler() {}
 
     /**
      * Gets the k<sup>th</sup> base 64 digit of {@code m}.
@@ -643,6 +556,17 @@ public abstract class MarsagliaTsangWangDiscreteSampler
     }
 
     /**
+     * Convert the probability to an integer in the range [0,2^30]. This is the numerator of
+     * a fraction with assumed denominator 2<sup>30</sup>.
+     *
+     * @param p Probability.
+     * @return the fraction numerator
+     */
+    private static int toUnsignedInt30(double p) {
+        return (int) (p * INT_30 + 0.5);
+    }
+
+    /**
      * Create a new instance for probabilities {@code p(i)} where the sample value {@code x} is
      * {@code i + offset}.
      *
@@ -655,10 +579,10 @@ public abstract class MarsagliaTsangWangDiscreteSampler
      * @param offset The offset (must be positive).
      * @return Sampler.
      */
-    private static MarsagliaTsangWangDiscreteSampler createSampler(UniformRandomProvider rng,
-                                                                   String distributionName,
-                                                                   int[] prob,
-                                                                   int offset) {
+    private static SharedStateDiscreteSampler createSampler(UniformRandomProvider rng,
+                                                            String distributionName,
+                                                            int[] prob,
+                                                            int offset) {
         // Note: No argument checks for private method.
 
         // Choose implementation based on the maximum index
@@ -673,426 +597,545 @@ public abstract class MarsagliaTsangWangDiscreteSampler
     }
 
     // =========================================================================
-    // The following factory methods are the public API to construct a sampler for:
-    // - Any discrete probability distribution (from provided double[] probabilities)
+    // The following public classes provide factory methods to construct a sampler for:
+    // - Enumerated probability distribution (from provided double[] probabilities)
     // - Poisson distribution for mean <= 1024
     // - Binomial distribution for trials <= 65535
     // =========================================================================
 
     /**
-     * Creates a sampler for a given probability distribution.
-     *
-     * <p>The probabilities will be normalised using their sum. The only requirement
-     * is the sum is positive.</p>
-     *
-     * <p>The sum of the probabilities is normalised to 2<sup>30</sup>. Note that
-     * probabilities are adjusted to the nearest 1<sup>-30</sup> due to round-off during
-     * the normalisation conversion. Consequently any probability less than 2<sup>-31</sup>
-     * will not be observed in samples.</p>
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param probabilities The list of probabilities.
-     * @return Sampler.
-     * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
-     * probability is negative, infinite or {@code NaN}, or the sum of all
-     * probabilities is not strictly positive.
+     * Create a sampler for an enumerated distribution of {@code n} values each with an
+     * associated probability.
+     * The samples corresponding to each probability are assumed to be a natural sequence
+     * starting at zero.
      */
-    public static MarsagliaTsangWangDiscreteSampler createDiscreteDistribution(UniformRandomProvider rng,
-                                                                               double[] probabilities) {
-        return createSampler(rng, DISCRETE_NAME, normaliseProbabilities(probabilities), 0);
-    }
+    public static final class Enumerated {
+        /** The name of the enumerated probability distribution. */
+        private static final String ENUMERATED_NAME = "Enumerated";
 
-    /**
-     * Normalise the probabilities to integers that sum to 2<sup>30</sup>.
-     *
-     * @param probabilities The list of probabilities.
-     * @return the normalised probabilities.
-     * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
-     * probability is negative, infinite or {@code NaN}, or the sum of all
-     * probabilities is not strictly positive.
-     */
-    private static int[] normaliseProbabilities(double[] probabilities) {
-        final double sumProb = InternalUtils.validateProbabilities(probabilities);
-
-        // Compute the normalisation: 2^30 / sum
-        final double normalisation = INT_30 / sumProb;
-        final int[] prob = new int[probabilities.length];
-        int sum = 0;
-        int max = 0;
-        int mode = 0;
-        for (int i = 0; i < prob.length; i++) {
-            // Add 0.5 for rounding
-            final int p = (int) (probabilities[i] * normalisation + 0.5);
-            sum += p;
-            // Find the mode (maximum probability)
-            if (max < p) {
-                max = p;
-                mode = i;
-            }
-            prob[i] = p;
-        }
+        /** Class contains only static methods. */
+        private Enumerated() {}
 
-        // The sum must be >= 2^30.
-        // Here just compensate the difference onto the highest probability.
-        prob[mode] += INT_30 - sum;
+        /**
+         * Creates a sampler for an enumerated distribution of {@code n} values each with an
+         * associated probability.
+         *
+         * <p>The probabilities will be normalised using their sum. The only requirement
+         * is the sum is positive.</p>
+         *
+         * <p>The sum of the probabilities is normalised to 2<sup>30</sup>. Note that
+         * probabilities are adjusted to the nearest 1<sup>-30</sup> due to round-off during
+         * the normalisation conversion. Consequently any probability less than 2<sup>-31</sup>
+         * will not be observed in samples.</p>
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param probabilities The list of probabilities.
+         * @return Sampler.
+         * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
+         * probability is negative, infinite or {@code NaN}, or the sum of all
+         * probabilities is not strictly positive.
+         */
+        public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                    double[] probabilities) {
+            return createSampler(rng, ENUMERATED_NAME, normaliseProbabilities(probabilities), 0);
+        }
 
-        return prob;
-    }
+        /**
+         * Normalise the probabilities to integers that sum to 2<sup>30</sup>.
+         *
+         * @param probabilities The list of probabilities.
+         * @return the normalised probabilities.
+         * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
+         * probability is negative, infinite or {@code NaN}, or the sum of all
+         * probabilities is not strictly positive.
+         */
+        private static int[] normaliseProbabilities(double[] probabilities) {
+            final double sumProb = InternalUtils.validateProbabilities(probabilities);
+
+            // Compute the normalisation: 2^30 / sum
+            final double normalisation = INT_30 / sumProb;
+            final int[] prob = new int[probabilities.length];
+            int sum = 0;
+            int max = 0;
+            int mode = 0;
+            for (int i = 0; i < prob.length; i++) {
+                // Add 0.5 for rounding
+                final int p = (int) (probabilities[i] * normalisation + 0.5);
+                sum += p;
+                // Find the mode (maximum probability)
+                if (max < p) {
+                    max = p;
+                    mode = i;
+                }
+                prob[i] = p;
+            }
 
-    /**
-     * Creates a sampler for the Poisson distribution.
-     *
-     * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
-     *
-     * <p>Storage requirements depend on the tabulated probability values. Example storage
-     * requirements are listed below.</p>
-     *
-     * <pre>
-     * mean      table size     kB
-     * 0.25      882            0.88
-     * 0.5       1135           1.14
-     * 1         1200           1.20
-     * 2         1451           1.45
-     * 4         1955           1.96
-     * 8         2961           2.96
-     * 16        4410           4.41
-     * 32        6115           6.11
-     * 64        8499           8.50
-     * 128       11528          11.53
-     * 256       15935          31.87
-     * 512       20912          41.82
-     * 1024      30614          61.23
-     * </pre>
-     *
-     * <p>Note: Storage changes to 2 bytes per index between {@code mean=128} and {@code mean=256}.</p>
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param mean Mean.
-     * @return Sampler.
-     * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
-     */
-    public static MarsagliaTsangWangDiscreteSampler createPoissonDistribution(UniformRandomProvider rng,
-                                                                              double mean) {
-        validatePoissonDistributionParameters(mean);
-
-        // Create the distribution either from X=0 or from X=mode when the mean is high.
-        return mean < POISSON_MEAN_THRESHOLD ?
-            createPoissonDistributionFromX0(rng, mean) :
-            createPoissonDistributionFromXMode(rng, mean);
-    }
+            // The sum must be >= 2^30.
+            // Here just compensate the difference onto the highest probability.
+            prob[mode] += INT_30 - sum;
 
-    /**
-     * Validate the Poisson distribution parameters.
-     *
-     * @param mean Mean.
-     * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
-     */
-    private static void validatePoissonDistributionParameters(double mean) {
-        if (mean <= 0) {
-            throw new IllegalArgumentException("mean is not strictly positive: " + mean);
-        }
-        if (mean > MAX_POISSON_MEAN) {
-            throw new IllegalArgumentException("mean " + mean + " > " + MAX_POISSON_MEAN);
+            return prob;
         }
     }
 
     /**
-     * Creates the Poisson distribution by computing probabilities recursively from {@code X=0}.
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param mean Mean.
-     * @return Sampler.
+     * Create a sampler for the Poisson distribution.
      */
-    private static MarsagliaTsangWangDiscreteSampler createPoissonDistributionFromX0(
-            UniformRandomProvider rng, double mean) {
-        final double p0 = Math.exp(-mean);
-
-        // Recursive update of Poisson probability until the value is too small
-        // p(x + 1) = p(x) * mean / (x + 1)
-        double p = p0;
-        int i;
-        for (i = 1; p * DOUBLE_31 >= 1; i++) {
-            p *= mean / i;
-        }
+    public static final class Poisson {
+        /** The name of the Poisson distribution. */
+        private static final String POISSON_NAME = "Poisson";
 
-        // Probabilities are 30-bit integers, assumed denominator 2^30
-        final int size = i - 1;
-        final int[] prob = new int[size];
-
-        p = p0;
-        prob[0] = toUnsignedInt30(p);
-        // The sum must exceed 2^30. In edges cases this is false due to round-off.
-        int sum = prob[0];
-        for (i = 1; i < prob.length; i++) {
-            p *= mean / i;
-            prob[i] = toUnsignedInt30(p);
-            sum += prob[i];
-        }
+        /**
+         * Upper bound on the mean for the Poisson distribution.
+         *
+         * <p>The original source code provided in Marsaglia, et al (2004) has no explicit
+         * limit but the code fails at mean >= 1941 as the transform to compute p(x=mode)
+         * produces infinity. Use a conservative limit of 1024.</p>
+         */
 
-        // If the sum is < 2^30 add the remaining sum to the mode (floor(mean)).
-        prob[(int) mean] += Math.max(0, INT_30 - sum);
+        private static final double MAX_MEAN = 1024;
+        /**
+         * The threshold for the mean of the Poisson distribution to switch the method used
+         * to compute the probabilities. This is taken from the example software provided by
+         * Marsaglia, et al (2004).
+         */
+        private static final double MEAN_THRESHOLD = 21.4;
 
-        // Note: offset = 0
-        return createSampler(rng, POISSON_NAME, prob, 0);
-    }
+        /** Class contains only static methods. */
+        private Poisson() {}
 
-    /**
-     * Creates the Poisson distribution by computing probabilities recursively upward and downward
-     * from {@code X=mode}, the location of the largest p-value.
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param mean Mean.
-     * @return Sampler.
-     */
-    private static MarsagliaTsangWangDiscreteSampler createPoissonDistributionFromXMode(
-            UniformRandomProvider rng, double mean) {
-        // If mean >= 21.4, generate from largest p-value up, then largest down.
-        // The largest p-value will be at the mode (floor(mean)).
-
-        // Find p(x=mode)
-        final int mode = (int) mean;
-        // This transform is stable until mean >= 1941 where p will result in Infinity
-        // before the divisor i is large enough to start reducing the product (i.e. i > c).
-        final double c = mean * Math.exp(-mean / mode);
-        double p = 1.0;
-        int i;
-        for (i = 1; i <= mode; i++) {
-            p *= c / i;
+        /**
+         * Creates a sampler for the Poisson distribution.
+         *
+         * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
+         *
+         * <p>Storage requirements depend on the tabulated probability values. Example storage
+         * requirements are listed below.</p>
+         *
+         * <pre>
+         * mean      table size     kB
+         * 0.25      882            0.88
+         * 0.5       1135           1.14
+         * 1         1200           1.20
+         * 2         1451           1.45
+         * 4         1955           1.96
+         * 8         2961           2.96
+         * 16        4410           4.41
+         * 32        6115           6.11
+         * 64        8499           8.50
+         * 128       11528          11.53
+         * 256       15935          31.87
+         * 512       20912          41.82
+         * 1024      30614          61.23
+         * </pre>
+         *
+         * <p>Note: Storage changes to 2 bytes per index between {@code mean=128} and {@code mean=256}.</p>
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param mean Mean.
+         * @return Sampler.
+         * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
+         */
+        public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                    double mean) {
+            validatePoissonDistributionParameters(mean);
+
+            // Create the distribution either from X=0 or from X=mode when the mean is high.
+            return mean < MEAN_THRESHOLD ?
+                createPoissonDistributionFromX0(rng, mean) :
+                createPoissonDistributionFromXMode(rng, mean);
         }
-        final double pMode = p;
 
-        // Find the upper limit using recursive computation of the p-value.
-        // Note this will exit when i overflows to negative so no check on the range
-        for (i = mode + 1; p * DOUBLE_31 >= 1; i++) {
-            p *= mean / i;
-        }
-        final int last = i - 2;
-
-        // Find the lower limit using recursive computation of the p-value.
-        p = pMode;
-        int j = -1;
-        for (i = mode - 1; i >= 0; i--) {
-            p *= (i + 1) / mean;
-            if (p * DOUBLE_31 < 1) {
-                j = i;
-                break;
+        /**
+         * Validate the Poisson distribution parameters.
+         *
+         * @param mean Mean.
+         * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
+         */
+        private static void validatePoissonDistributionParameters(double mean) {
+            if (mean <= 0) {
+                throw new IllegalArgumentException("mean is not strictly positive: " + mean);
+            }
+            if (mean > MAX_MEAN) {
+                throw new IllegalArgumentException("mean " + mean + " > " + MAX_MEAN);
             }
         }
 
-        // Probabilities are 30-bit integers, assumed denominator 2^30.
-        // This is the minimum sample value: prob[x - offset] = p(x)
-        final int offset = j + 1;
-        final int size = last - offset + 1;
-        final int[] prob = new int[size];
-
-        p = pMode;
-        prob[mode - offset] = toUnsignedInt30(p);
-        // The sum must exceed 2^30. In edges cases this is false due to round-off.
-        int sum = prob[mode - offset];
-        // From mode to upper limit
-        for (i = mode + 1; i <= last; i++) {
-            p *= mean / i;
-            prob[i - offset] = toUnsignedInt30(p);
-            sum += prob[i - offset];
-        }
-        // From mode to lower limit
-        p = pMode;
-        for (i = mode - 1; i >= offset; i--) {
-            p *= (i + 1) / mean;
-            prob[i - offset] = toUnsignedInt30(p);
-            sum += prob[i - offset];
+        /**
+         * Creates the Poisson distribution by computing probabilities recursively from {@code X=0}.
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param mean Mean.
+         * @return Sampler.
+         */
+        private static SharedStateDiscreteSampler createPoissonDistributionFromX0(
+                UniformRandomProvider rng, double mean) {
+            final double p0 = Math.exp(-mean);
+
+            // Recursive update of Poisson probability until the value is too small
+            // p(x + 1) = p(x) * mean / (x + 1)
+            double p = p0;
+            int i;
+            for (i = 1; p * DOUBLE_31 >= 1; i++) {
+                p *= mean / i;
+            }
+
+            // Probabilities are 30-bit integers, assumed denominator 2^30
+            final int size = i - 1;
+            final int[] prob = new int[size];
+
+            p = p0;
+            prob[0] = toUnsignedInt30(p);
+            // The sum must exceed 2^30. In edges cases this is false due to round-off.
+            int sum = prob[0];
+            for (i = 1; i < prob.length; i++) {
+                p *= mean / i;
+                prob[i] = toUnsignedInt30(p);
+                sum += prob[i];
+            }
+
+            // If the sum is < 2^30 add the remaining sum to the mode (floor(mean)).
+            prob[(int) mean] += Math.max(0, INT_30 - sum);
+
+            // Note: offset = 0
+            return createSampler(rng, POISSON_NAME, prob, 0);
         }
 
-        // If the sum is < 2^30 add the remaining sum to the mode.
-        // If above 2^30 then the effect is truncation of the long tail of the distribution.
-        prob[mode - offset] += Math.max(0, INT_30 - sum);
+        /**
+         * Creates the Poisson distribution by computing probabilities recursively upward and downward
+         * from {@code X=mode}, the location of the largest p-value.
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param mean Mean.
+         * @return Sampler.
+         */
+        private static SharedStateDiscreteSampler createPoissonDistributionFromXMode(
+                UniformRandomProvider rng, double mean) {
+            // If mean >= 21.4, generate from largest p-value up, then largest down.
+            // The largest p-value will be at the mode (floor(mean)).
+
+            // Find p(x=mode)
+            final int mode = (int) mean;
+            // This transform is stable until mean >= 1941 where p will result in Infinity
+            // before the divisor i is large enough to start reducing the product (i.e. i > c).
+            final double c = mean * Math.exp(-mean / mode);
+            double p = 1.0;
+            int i;
+            for (i = 1; i <= mode; i++) {
+                p *= c / i;
+            }
+            final double pMode = p;
+
+            // Find the upper limit using recursive computation of the p-value.
+            // Note this will exit when i overflows to negative so no check on the range
+            for (i = mode + 1; p * DOUBLE_31 >= 1; i++) {
+                p *= mean / i;
+            }
+            final int last = i - 2;
+
+            // Find the lower limit using recursive computation of the p-value.
+            p = pMode;
+            int j = -1;
+            for (i = mode - 1; i >= 0; i--) {
+                p *= (i + 1) / mean;
+                if (p * DOUBLE_31 < 1) {
+                    j = i;
+                    break;
+                }
+            }
 
-        return createSampler(rng, POISSON_NAME, prob, offset);
+            // Probabilities are 30-bit integers, assumed denominator 2^30.
+            // This is the minimum sample value: prob[x - offset] = p(x)
+            final int offset = j + 1;
+            final int size = last - offset + 1;
+            final int[] prob = new int[size];
+
+            p = pMode;
+            prob[mode - offset] = toUnsignedInt30(p);
+            // The sum must exceed 2^30. In edges cases this is false due to round-off.
+            int sum = prob[mode - offset];
+            // From mode to upper limit
+            for (i = mode + 1; i <= last; i++) {
+                p *= mean / i;
+                prob[i - offset] = toUnsignedInt30(p);
+                sum += prob[i - offset];
+            }
+            // From mode to lower limit
+            p = pMode;
+            for (i = mode - 1; i >= offset; i--) {
+                p *= (i + 1) / mean;
+                prob[i - offset] = toUnsignedInt30(p);
+                sum += prob[i - offset];
+            }
+
+            // If the sum is < 2^30 add the remaining sum to the mode.
+            // If above 2^30 then the effect is truncation of the long tail of the distribution.
+            prob[mode - offset] += Math.max(0, INT_30 - sum);
+
+            return createSampler(rng, POISSON_NAME, prob, offset);
+        }
     }
 
     /**
-     * Creates a sampler for the Binomial distribution.
-     *
-     * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
-     *
-     * <p>Storage requirements depend on the tabulated probability values. Example storage
-     * requirements are listed below (in kB).</p>
-     *
-     * <pre>
-     *          p
-     * trials   0.5    0.1   0.01  0.001
-     *    4    0.06   0.63   0.44   0.44
-     *   16    0.69   1.14   0.76   0.44
-     *   64    4.73   2.40   1.14   0.51
-     *  256    8.63   5.17   1.89   0.82
-     * 1024   31.12   9.45   3.34   0.89
-     * </pre>
-     *
-     * <p>The method requires that the Binomial distribution probability at {@code x=0} can be computed.
-     * This will fail when {@code (1 - p)^trials == 0} which requires {@code trials} to be large
-     * and/or {@code p} to be small. In this case an exception is raised.</p>
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param trials Number of trials.
-     * @param probabilityOfSuccess Probability of success (p).
-     * @return Sampler.
-     * @throws IllegalArgumentException if {@code trials < 0} or {@code trials >= 2^16},
-     * {@code p} is not in the range {@code [0-1]}, or the probability distribution cannot
-     * be computed.
+     * Create a sampler for the Binomial distribution.
      */
-    public static MarsagliaTsangWangDiscreteSampler createBinomialDistribution(UniformRandomProvider rng,
-                                                                               int trials,
-                                                                               double probabilityOfSuccess) {
-        validateBinomialDistributionParameters(trials, probabilityOfSuccess);
-
-        // Handle edge cases
-        if (probabilityOfSuccess == 0) {
-            return new MarsagliaTsangWangFixedResultBinomialSampler(0);
-        }
-        if (probabilityOfSuccess == 1) {
-            return new MarsagliaTsangWangFixedResultBinomialSampler(trials);
-        }
+    public static final class Binomial {
+        /** The name of the Binomial distribution. */
+        private static final String BINOMIAL_NAME = "Binomial";
+
+        /**
+         * Return a fixed result for the Binomial distribution. This is a special class to handle
+         * an edge case of probability of success equal to 0 or 1.
+         */
+        private static class MarsagliaTsangWangFixedResultBinomialSampler
+            extends AbstractMarsagliaTsangWangDiscreteSampler {
+            /** The result. */
+            private final int result;
+
+            /**
+             * @param result Result.
+             */
+            MarsagliaTsangWangFixedResultBinomialSampler(int result) {
+                super(null, BINOMIAL_NAME);
+                this.result = result;
+            }
 
-        // Check the supported size.
-        if (trials >= INT_16) {
-            throw new IllegalArgumentException("Unsupported number of trials: " + trials);
+            @Override
+            public int sample() {
+                return result;
+            }
+
+            @Override
+            public String toString() {
+                return BINOMIAL_NAME + " deviate";
+            }
+
+            @Override
+            public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+                // No shared state
+                return this;
+            }
         }
 
-        return createBinomialDistributionSampler(rng, trials, probabilityOfSuccess);
-    }
+        /**
+         * Return an inversion result for the Binomial distribution. This assumes the
+         * following:
+         *
+         * <pre>
+         * Binomial(n, p) = 1 - Binomial(n, 1 - p)
+         * </pre>
+         */
+        private static class MarsagliaTsangWangInversionBinomialSampler
+            extends AbstractMarsagliaTsangWangDiscreteSampler {
+            /** The number of trials. */
+            private final int trials;
+            /** The Binomial distribution sampler. */
+            private final SharedStateDiscreteSampler sampler;
+
+            /**
+             * @param trials Number of trials.
+             * @param sampler Binomial distribution sampler.
+             */
+            MarsagliaTsangWangInversionBinomialSampler(int trials,
+                                                       SharedStateDiscreteSampler sampler) {
+                super(null, BINOMIAL_NAME);
+                this.trials = trials;
+                this.sampler = sampler;
+            }
 
-    /**
-     * Validate the Binomial distribution parameters.
-     *
-     * @param trials Number of trials.
-     * @param probabilityOfSuccess Probability of success (p).
-     * @throws IllegalArgumentException if {@code trials < 0} or
-     * {@code p} is not in the range {@code [0-1]}
-     */
-    private static void validateBinomialDistributionParameters(int trials, double probabilityOfSuccess) {
-        if (trials < 0) {
-            throw new IllegalArgumentException("Trials is not positive: " + trials);
+            @Override
+            public int sample() {
+                return trials - sampler.sample();
+            }
+
+            @Override
+            public String toString() {
+                return sampler.toString();
+            }
+
+            @Override
+            public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+                return new MarsagliaTsangWangInversionBinomialSampler(this.trials,
+                    this.sampler.withUniformRandomProvider(rng));
+            }
         }
-        if (probabilityOfSuccess < 0 || probabilityOfSuccess > 1) {
-            throw new IllegalArgumentException("Probability is not in range [0,1]: " + probabilityOfSuccess);
+
+        /** Class contains only static methods. */
+        private Binomial() {}
+
+        /**
+         * Creates a sampler for the Binomial distribution.
+         *
+         * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
+         *
+         * <p>Storage requirements depend on the tabulated probability values. Example storage
+         * requirements are listed below (in kB).</p>
+         *
+         * <pre>
+         *          p
+         * trials   0.5    0.1   0.01  0.001
+         *    4    0.06   0.63   0.44   0.44
+         *   16    0.69   1.14   0.76   0.44
+         *   64    4.73   2.40   1.14   0.51
+         *  256    8.63   5.17   1.89   0.82
+         * 1024   31.12   9.45   3.34   0.89
+         * </pre>
+         *
+         * <p>The method requires that the Binomial distribution probability at {@code x=0} can be computed.
+         * This will fail when {@code (1 - p)^trials == 0} which requires {@code trials} to be large
+         * and/or {@code p} to be small. In this case an exception is raised.</p>
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param trials Number of trials.
+         * @param probabilityOfSuccess Probability of success (p).
+         * @return Sampler.
+         * @throws IllegalArgumentException if {@code trials < 0} or {@code trials >= 2^16},
+         * {@code p} is not in the range {@code [0-1]}, or the probability distribution cannot
+         * be computed.
+         */
+        public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+                                                    int trials,
+                                                    double probabilityOfSuccess) {
+            validateBinomialDistributionParameters(trials, probabilityOfSuccess);
+
+            // Handle edge cases
+            if (probabilityOfSuccess == 0) {
+                return new MarsagliaTsangWangFixedResultBinomialSampler(0);
+            }
+            if (probabilityOfSuccess == 1) {
+                return new MarsagliaTsangWangFixedResultBinomialSampler(trials);
+            }
+
+            // Check the supported size.
+            if (trials >= INT_16) {
+                throw new IllegalArgumentException("Unsupported number of trials: " + trials);
+            }
+
+            return createBinomialDistributionSampler(rng, trials, probabilityOfSuccess);
         }
-    }
 
-    /**
-     * Creates the Binomial distribution sampler.
-     *
-     * <p>This assumes the parameters for the distribution are valid. The method
-     * will only fail if the initial probability for {@code X=0} is zero.</p>
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param trials Number of trials.
-     * @param probabilityOfSuccess Probability of success (p).
-     * @return Sampler.
-     * @throws IllegalArgumentException if the probability distribution cannot be
-     * computed.
-     */
-    private static MarsagliaTsangWangDiscreteSampler createBinomialDistributionSampler(
-            UniformRandomProvider rng, int trials, double probabilityOfSuccess) {
-
-        // The maximum supported value for Math.exp is approximately -744.
-        // This occurs when trials is large and p is close to 1.
-        // Handle this by using an inversion: generate j=Binomial(n,1-p), return n-j
-        final boolean useInversion = probabilityOfSuccess > 0.5;
-        final double p = useInversion ? 1 - probabilityOfSuccess : probabilityOfSuccess;
-
-        // Check if the distribution can be computed
-        final double p0 = Math.exp(trials * Math.log(1 - p));
-        if (p0 < Double.MIN_VALUE) {
-            throw new IllegalArgumentException("Unable to compute distribution");
+        /**
+         * Validate the Binomial distribution parameters.
+         *
+         * @param trials Number of trials.
+         * @param probabilityOfSuccess Probability of success (p).
+         * @throws IllegalArgumentException if {@code trials < 0} or
+         * {@code p} is not in the range {@code [0-1]}
+         */
+        private static void validateBinomialDistributionParameters(int trials, double probabilityOfSuccess) {
+            if (trials < 0) {
+                throw new IllegalArgumentException("Trials is not positive: " + trials);
+            }
+            if (probabilityOfSuccess < 0 || probabilityOfSuccess > 1) {
+                throw new IllegalArgumentException("Probability is not in range [0,1]: " + probabilityOfSuccess);
+            }
         }
 
-        // First find size of probability array
-        double t = p0;
-        final double h = p / (1 - p);
-        // Find first probability above the threshold of 2^-31
-        int begin = 0;
-        if (t * DOUBLE_31 < 1) {
-            // Somewhere after p(0)
-            // Note:
-            // If this loop is entered p(0) is < 2^-31.
-            // This has been tested at the extreme for p(0)=Double.MIN_VALUE and either
-            // p=0.5 or trials=2^16-1 and does not fail to find the beginning.
-            for (int i = 1; i <= trials; i++) {
+        /**
+         * Creates the Binomial distribution sampler.
+         *
+         * <p>This assumes the parameters for the distribution are valid. The method
+         * will only fail if the initial probability for {@code X=0} is zero.</p>
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param trials Number of trials.
+         * @param probabilityOfSuccess Probability of success (p).
+         * @return Sampler.
+         * @throws IllegalArgumentException if the probability distribution cannot be
+         * computed.
+         */
+        private static SharedStateDiscreteSampler createBinomialDistributionSampler(
+                UniformRandomProvider rng, int trials, double probabilityOfSuccess) {
+
+            // The maximum supported value for Math.exp is approximately -744.
+            // This occurs when trials is large and p is close to 1.
+            // Handle this by using an inversion: generate j=Binomial(n,1-p), return n-j
+            final boolean useInversion = probabilityOfSuccess > 0.5;
+            final double p = useInversion ? 1 - probabilityOfSuccess : probabilityOfSuccess;
+
+            // Check if the distribution can be computed
+            final double p0 = Math.exp(trials * Math.log(1 - p));
+            if (p0 < Double.MIN_VALUE) {
+                throw new IllegalArgumentException("Unable to compute distribution");
+            }
+
+            // First find size of probability array
+            double t = p0;
+            final double h = p / (1 - p);
+            // Find first probability above the threshold of 2^-31
+            int begin = 0;
+            if (t * DOUBLE_31 < 1) {
+                // Somewhere after p(0)
+                // Note:
+                // If this loop is entered p(0) is < 2^-31.
+                // This has been tested at the extreme for p(0)=Double.MIN_VALUE and either
+                // p=0.5 or trials=2^16-1 and does not fail to find the beginning.
+                for (int i = 1; i <= trials; i++) {
+                    t *= (trials + 1 - i) * h / i;
+                    if (t * DOUBLE_31 >= 1) {
+                        begin = i;
+                        break;
+                    }
+                }
+            }
+            // Find last probability
+            int end = trials;
+            for (int i = begin + 1; i <= trials; i++) {
                 t *= (trials + 1 - i) * h / i;
-                if (t * DOUBLE_31 >= 1) {
-                    begin = i;
+                if (t * DOUBLE_31 < 1) {
+                    end = i - 1;
                     break;
                 }
             }
-        }
-        // Find last probability
-        int end = trials;
-        for (int i = begin + 1; i <= trials; i++) {
-            t *= (trials + 1 - i) * h / i;
-            if (t * DOUBLE_31 < 1) {
-                end = i - 1;
-                break;
-            }
-        }
-
-        return createBinomialDistributionSamplerFromRange(rng, trials, p, useInversion,
-                p0, begin, end);
-    }
 
-    /**
-     * Creates the Binomial distribution sampler using only the probability values for {@code X}
-     * between the begin and the end (inclusive).
-     *
-     * @param rng Generator of uniformly distributed random numbers.
-     * @param trials Number of trials.
-     * @param p Probability of success (p).
-     * @param useInversion Set to {@code true} if the probability was inverted.
-     * @param p0 Probability at {@code X=0}
-     * @param begin Begin value {@code X} for the distribution.
-     * @param end End value {@code X} for the distribution.
-     * @return Sampler.
-     */
-    private static MarsagliaTsangWangDiscreteSampler createBinomialDistributionSamplerFromRange(
-            UniformRandomProvider rng, int trials, double p,
-            boolean useInversion, double p0, int begin, int end) {
-
-        // Assign probability values as 30-bit integers
-        final int size = end - begin + 1;
-        final int[] prob = new int[size];
-        double t = p0;
-        final double h = p / (1 - p);
-        for (int i = 1; i <= begin; i++) {
-            t *= (trials + 1 - i) * h / i;
-        }
-        int sum = toUnsignedInt30(t);
-        prob[0] = sum;
-        for (int i = begin + 1; i <= end; i++) {
-            t *= (trials + 1 - i) * h / i;
-            prob[i - begin] = toUnsignedInt30(t);
-            sum += prob[i - begin];
+            return createBinomialDistributionSamplerFromRange(rng, trials, p, useInversion,
+                    p0, begin, end);
         }
 
-        // If the sum is < 2^30 add the remaining sum to the mode (floor((n+1)p))).
-        // If above 2^30 then the effect is truncation of the long tail of the distribution.
-        final int mode = (int) ((trials + 1) * p) - begin;
-        prob[mode] += Math.max(0, INT_30 - sum);
+        /**
+         * Creates the Binomial distribution sampler using only the probability values for {@code X}
+         * between the begin and the end (inclusive).
+         *
+         * @param rng Generator of uniformly distributed random numbers.
+         * @param trials Number of trials.
+         * @param p Probability of success (p).
+         * @param useInversion Set to {@code true} if the probability was inverted.
+         * @param p0 Probability at {@code X=0}
+         * @param begin Begin value {@code X} for the distribution.
+         * @param end End value {@code X} for the distribution.
+         * @return Sampler.
+         */
+        private static SharedStateDiscreteSampler createBinomialDistributionSamplerFromRange(
+                UniformRandomProvider rng, int trials, double p,
+                boolean useInversion, double p0, int begin, int end) {
+
+            // Assign probability values as 30-bit integers
+            final int size = end - begin + 1;
+            final int[] prob = new int[size];
+            double t = p0;
+            final double h = p / (1 - p);
+            for (int i = 1; i <= begin; i++) {
+                t *= (trials + 1 - i) * h / i;
+            }
+            int sum = toUnsignedInt30(t);
+            prob[0] = sum;
+            for (int i = begin + 1; i <= end; i++) {
+                t *= (trials + 1 - i) * h / i;
+                prob[i - begin] = toUnsignedInt30(t);
+                sum += prob[i - begin];
+            }
 
-        final MarsagliaTsangWangDiscreteSampler sampler = createSampler(rng, BINOMIAL_NAME, prob, begin);
+            // If the sum is < 2^30 add the remaining sum to the mode (floor((n+1)p))).
+            // If above 2^30 then the effect is truncation of the long tail of the distribution.
+            final int mode = (int) ((trials + 1) * p) - begin;
+            prob[mode] += Math.max(0, INT_30 - sum);
 
-        // Check if an inversion was made
-        return useInversion ?
-               new MarsagliaTsangWangInversionBinomialSampler(trials, sampler) :
-               sampler;
-    }
+            final SharedStateDiscreteSampler sampler = createSampler(rng, BINOMIAL_NAME, prob, begin);
 
-    /**
-     * Convert the probability to an integer in the range [0,2^30]. This is the numerator of
-     * a fraction with assumed denominator 2<sup>30</sup>.
-     *
-     * @param p Probability.
-     * @return the fraction numerator
-     */
-    private static int toUnsignedInt30(double p) {
-        return (int) (p * INT_30 + 0.5);
+            // Check if an inversion was made
+            return useInversion ?
+                   new MarsagliaTsangWangInversionBinomialSampler(trials, sampler) :
+                   sampler;
+        }
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
index e5f9108..0264c7c 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
@@ -68,7 +68,7 @@ public class AliasMethodDiscreteSamplerTest {
 
     @Test
     public void testToString() {
-        final AliasMethodDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5});
+        final SharedStateDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5});
         Assert.assertTrue(sampler.toString().toLowerCase().contains("alias method"));
     }
 
@@ -78,9 +78,9 @@ public class AliasMethodDiscreteSamplerTest {
      * @param probabilities the probabilities
      * @return the alias method discrete sampler
      */
-    private static AliasMethodDiscreteSampler createSampler(double[] probabilities) {
+    private static SharedStateDiscreteSampler createSampler(double[] probabilities) {
         final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        return AliasMethodDiscreteSampler.create(rng, probabilities, -1);
+        return AliasMethodDiscreteSampler.of(rng, probabilities, -1);
     }
 
     /**
@@ -130,7 +130,7 @@ public class AliasMethodDiscreteSamplerTest {
     @Test
     public void testNonUniformSamplesWithProbabilitiesWithDefaultFactoryConstructor() {
         final double[] expected = {0.1, 0.2, 0.3, 0.1, 0.3 };
-        checkSamples(AliasMethodDiscreteSampler.create(RandomSource.create(RandomSource.SPLIT_MIX_64), expected), expected);
+        checkSamples(AliasMethodDiscreteSampler.of(RandomSource.create(RandomSource.SPLIT_MIX_64), expected), expected);
     }
 
     /**
@@ -218,7 +218,7 @@ public class AliasMethodDiscreteSamplerTest {
      *
      * @param expected the expected probabilities
      */
-    private static void checkSamples(AliasMethodDiscreteSampler sampler, double[] probabilies) {
+    private static void checkSamples(SharedStateDiscreteSampler sampler, double[] probabilies) {
         final int numberOfSamples = 10000;
         final long[] samples = new long[probabilies.length];
         for (int i = 0; i < numberOfSamples; i++) {
@@ -275,8 +275,8 @@ public class AliasMethodDiscreteSamplerTest {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         // Use negative alpha to disable padding
-        final AliasMethodDiscreteSampler sampler1 =
-            AliasMethodDiscreteSampler.create(rng1, probabilities, -1);
+        final SharedStateDiscreteSampler sampler1 =
+            AliasMethodDiscreteSampler.of(rng1, probabilities, -1);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
index f6fe40e..73c7cb7 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
@@ -53,12 +53,12 @@ public final class DiscreteSamplersList {
             add(LIST, new org.apache.commons.math3.distribution.BinomialDistribution(unusedRng, trialsBinomial, probSuccessBinomial),
                 // range [9,16]
                 MathArrays.sequence(8, 9, 1),
-                MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(RandomSource.create(RandomSource.WELL_19937_A), trialsBinomial, probSuccessBinomial));
+                MarsagliaTsangWangDiscreteSampler.Binomial.of(RandomSource.create(RandomSource.WELL_19937_A), trialsBinomial, probSuccessBinomial));
             // Inverted
             add(LIST, new org.apache.commons.math3.distribution.BinomialDistribution(unusedRng, trialsBinomial, 1 - probSuccessBinomial),
                 // range [4,11] = [20-16, 20-9]
                 MathArrays.sequence(8, 4, 1),
-                MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(RandomSource.create(RandomSource.WELL_19937_C), trialsBinomial, 1 - probSuccessBinomial));
+                MarsagliaTsangWangDiscreteSampler.Binomial.of(RandomSource.create(RandomSource.WELL_19937_C), trialsBinomial, 1 - probSuccessBinomial));
 
             // Geometric ("inverse method").
             final double probSuccessGeometric = 0.21;
@@ -68,7 +68,7 @@ public final class DiscreteSamplersList {
             // Geometric.
             add(LIST, new org.apache.commons.math3.distribution.GeometricDistribution(unusedRng, probSuccessGeometric),
                 MathArrays.sequence(10, 0, 1),
-                new GeometricSampler(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), probSuccessGeometric));
+                GeometricSampler.of(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), probSuccessGeometric));
 
             // Hypergeometric ("inverse method").
             final int popSizeHyper = 34;
@@ -136,10 +136,10 @@ public final class DiscreteSamplersList {
                 new SmallMeanPoissonSampler(RandomSource.create(RandomSource.XO_SHI_RO_256_PLUS), meanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(10, 0, 1),
-                new KempSmallMeanPoissonSampler(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
+                KempSmallMeanPoissonSampler.of(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
                     MathArrays.sequence(10, 0, 1),
-                    MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
+                    MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
             // Poisson (40 < mean < 80).
             final double largeMeanPoisson = 67.89;
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
@@ -151,7 +151,7 @@ public final class DiscreteSamplersList {
                 new LargeMeanPoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), largeMeanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(50, (int) (largeMeanPoisson - 25), 1),
-                MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PLUS), largeMeanPoisson));
+                MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PLUS), largeMeanPoisson));
             // Poisson (mean >> 40).
             final double veryLargeMeanPoisson = 543.21;
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
@@ -163,16 +163,16 @@ public final class DiscreteSamplersList {
                 new LargeMeanPoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), veryLargeMeanPoisson));
             add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
                 MathArrays.sequence(100, (int) (veryLargeMeanPoisson - 50), 1),
-                MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(RandomSource.create(RandomSource.XO_RO_SHI_RO_64_SS), veryLargeMeanPoisson));
+                MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_RO_SHI_RO_64_SS), veryLargeMeanPoisson));
 
             // Any discrete distribution
             final double[] discreteProbabilities = new double[] {0.1, 0.2, 0.3, 0.4, 0.5};
             add(LIST, discreteProbabilities,
-                MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(RandomSource.create(RandomSource.XO_SHI_RO_512_PLUS), discreteProbabilities));
+                MarsagliaTsangWangDiscreteSampler.Enumerated.of(RandomSource.create(RandomSource.XO_SHI_RO_512_PLUS), discreteProbabilities));
             add(LIST, discreteProbabilities,
-                new GuideTableDiscreteSampler(RandomSource.create(RandomSource.XO_SHI_RO_512_SS), discreteProbabilities));
+                GuideTableDiscreteSampler.of(RandomSource.create(RandomSource.XO_SHI_RO_512_SS), discreteProbabilities));
             add(LIST, discreteProbabilities,
-                    AliasMethodDiscreteSampler.create(RandomSource.create(RandomSource.KISS), discreteProbabilities));
+                    AliasMethodDiscreteSampler.of(RandomSource.create(RandomSource.KISS), discreteProbabilities));
         } catch (Exception e) {
             // CHECKSTYLE: stop Regexp
             System.err.println("Unexpected exception while creating the list of samplers: " + e);
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
index f384485..baaa77f 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
@@ -33,7 +33,7 @@ public class GeometricSamplerTest {
     @Test
     public void testProbabilityOfSuccessIsOneGeneratesZeroForSamples() {
         final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        final GeometricSampler sampler = new GeometricSampler(rng, 1);
+        final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, 1);
         // All samples should be 0
         for (int i = 0; i < 10; i++) {
             Assert.assertEquals("p=1 should have 0 for all samples", 0, sampler.sample());
@@ -56,7 +56,7 @@ public class GeometricSamplerTest {
         // The internal exponential sampler validates the mean so demonstrate creating a
         // geometric sampler does not throw.
         final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        new GeometricSampler(rng, probabilityOfSuccess);
+        GeometricSampler.of(rng, probabilityOfSuccess);
     }
 
     /**
@@ -66,7 +66,7 @@ public class GeometricSamplerTest {
     @Test
     public void testProbabilityOfSuccessIsOneSamplerToString() {
         final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        final GeometricSampler sampler = new GeometricSampler(unusedRng, 1);
+        final SharedStateDiscreteSampler sampler = GeometricSampler.of(unusedRng, 1);
         Assert.assertTrue("Missing 'Geometric' from toString",
             sampler.toString().contains("Geometric"));
     }
@@ -83,7 +83,7 @@ public class GeometricSamplerTest {
     @Test
     public void testProbabilityOfSuccessIsAlmostZeroGeneratesMaxValueForSamples() {
         final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        final GeometricSampler sampler = new GeometricSampler(rng, Double.MIN_VALUE);
+        final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, Double.MIN_VALUE);
         // All samples should be max value
         for (int i = 0; i < 10; i++) {
             Assert.assertEquals("p=(almost 0) should have Integer.MAX_VALUE for all samples",
@@ -98,8 +98,7 @@ public class GeometricSamplerTest {
     public void testProbabilityOfSuccessAboveOneThrows() {
         final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double probabilityOfSuccess = Math.nextUp(1.0);
-        @SuppressWarnings("unused")
-        final GeometricSampler sampler = new GeometricSampler(unusedRng, probabilityOfSuccess);
+        GeometricSampler.of(unusedRng, probabilityOfSuccess);
     }
 
     /**
@@ -109,8 +108,7 @@ public class GeometricSamplerTest {
     public void testProbabilityOfSuccessIsZeroThrows() {
         final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
         final double probabilityOfSuccess = 0;
-        @SuppressWarnings("unused")
-        final GeometricSampler sampler = new GeometricSampler(unusedRng, probabilityOfSuccess);
+        GeometricSampler.of(unusedRng, probabilityOfSuccess);
     }
 
     /**
@@ -138,8 +136,8 @@ public class GeometricSamplerTest {
     private static void testSharedStateSampler(double probabilityOfSuccess) {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final GeometricSampler sampler1 =
-            new GeometricSampler(rng1, probabilityOfSuccess);
+        final SharedStateDiscreteSampler sampler1 =
+            GeometricSampler.of(rng1, probabilityOfSuccess);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
index e38b3db..fc29e0e 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
@@ -76,7 +76,7 @@ public class GuideTableDiscreteSamplerTest {
 
     @Test
     public void testToString() {
-        final GuideTableDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5}, 1.0);
+        final SharedStateDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5}, 1.0);
         Assert.assertTrue(sampler.toString().toLowerCase().contains("guide table"));
     }
 
@@ -86,9 +86,9 @@ public class GuideTableDiscreteSamplerTest {
      * @param probabilities the probabilities
      * @return the alias method discrete sampler
      */
-    private static GuideTableDiscreteSampler createSampler(double[] probabilities, double alpha) {
+    private static SharedStateDiscreteSampler createSampler(double[] probabilities, double alpha) {
         final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
-        return new GuideTableDiscreteSampler(rng, probabilities, alpha);
+        return GuideTableDiscreteSampler.of(rng, probabilities, alpha);
     }
 
     /**
@@ -201,7 +201,7 @@ public class GuideTableDiscreteSamplerTest {
      * @param alpha the alpha
      */
     private static void checkSamples(double[] probabilies, double alpha) {
-        final GuideTableDiscreteSampler sampler = createSampler(probabilies, alpha);
+        final SharedStateDiscreteSampler sampler = createSampler(probabilies, alpha);
 
         final int numberOfSamples = 10000;
         final long[] samples = new long[probabilies.length];
@@ -244,8 +244,8 @@ public class GuideTableDiscreteSamplerTest {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double[] probabilities = {0.1, 0, 0.2, 0.3, 0.1, 0.3, 0};
-        final GuideTableDiscreteSampler sampler1 =
-            new GuideTableDiscreteSampler(rng1, probabilities);
+        final SharedStateDiscreteSampler sampler1 =
+            GuideTableDiscreteSampler.of(rng1, probabilities);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
index 2796ff0..cc0b7c8 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
@@ -46,7 +46,7 @@ public class KempSmallMeanPoissonSamplerTest {
     public void testConstructorThrowsWithMeanLargerThanUpperBound() {
         final double mean = SUPPORTED_UPPER_BOUND + 1;
         @SuppressWarnings("unused")
-        KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+        SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
     }
 
     /**
@@ -56,7 +56,7 @@ public class KempSmallMeanPoissonSamplerTest {
     public void testConstructorThrowsWithZeroMean() {
         final double mean = 0;
         @SuppressWarnings("unused")
-        KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+        SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
     }
 
     /**
@@ -66,7 +66,7 @@ public class KempSmallMeanPoissonSamplerTest {
     public void testConstructorThrowsWithNegativeMean() {
         final double mean = -1;
         @SuppressWarnings("unused")
-        KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+        SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
     }
 
     /**
@@ -76,7 +76,7 @@ public class KempSmallMeanPoissonSamplerTest {
     public void testConstructorWithNaNMean() {
         final double mean = Double.NaN;
         @SuppressWarnings("unused")
-        KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+        SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
     }
 
     /**
@@ -129,7 +129,7 @@ public class KempSmallMeanPoissonSamplerTest {
             PoissonDistribution.DEFAULT_EPSILON, PoissonDistribution.DEFAULT_MAX_ITERATIONS);
 
         final FixedRNG rng = new FixedRNG(0);
-        final KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(rng, mean);
+        final SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(rng, mean);
 
         // Lower bound should be zero
         testSample(rng, sampler, 0, 0, 0);
@@ -154,8 +154,8 @@ public class KempSmallMeanPoissonSamplerTest {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final double mean = 1.23;
-        final KempSmallMeanPoissonSampler sampler1 =
-            new KempSmallMeanPoissonSampler(rng1, mean);
+        final SharedStateDiscreteSampler sampler1 =
+            KempSmallMeanPoissonSampler.of(rng1, mean);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
@@ -169,7 +169,7 @@ public class KempSmallMeanPoissonSamplerTest {
      * @param lower the expected lower limit
      * @param upper the expected upper limit
      */
-    private static void testSample(FixedRNG rng, KempSmallMeanPoissonSampler sampler, double cumulativeProbability,
+    private static void testSample(FixedRNG rng, SharedStateDiscreteSampler sampler, double cumulativeProbability,
         int lower, int upper) {
         rng.setValue(cumulativeProbability);
         final int sample = sampler.sample();
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
index 5f2dd87..4b05b1e 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
@@ -85,9 +85,9 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
      * @param probabilities Probabilities.
      * @return the sampler
      */
-    private static MarsagliaTsangWangDiscreteSampler createDiscreteDistributionSampler(double[] probabilities) {
+    private static SharedStateDiscreteSampler createDiscreteDistributionSampler(double[] probabilities) {
         final UniformRandomProvider rng = new SplitMix64(0L);
-        return MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng, probabilities);
+        return MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, probabilities);
     }
 
     // Sampling tests
@@ -146,9 +146,9 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final double[] p2 = createProbabilities(offset2, prob);
         final double[] p3 = createProbabilities(offset3, prob);
 
-        final MarsagliaTsangWangDiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng1, p1);
-        final MarsagliaTsangWangDiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng2, p2);
-        final MarsagliaTsangWangDiscreteSampler sampler3 = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng3, p3);
+        final SharedStateDiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng1, p1);
+        final SharedStateDiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng2, p2);
+        final SharedStateDiscreteSampler sampler3 = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng3, p3);
 
         for (int i = 0; i < values.length; i++) {
             // Remove offsets
@@ -189,12 +189,12 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
 
         // First test the table is completely filled to 2^30
         final UniformRandomProvider dummyRng = new FixedSequenceIntProvider(new int[] {0xffffffff});
-        final MarsagliaTsangWangDiscreteSampler dummySampler = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(dummyRng, probabilities);
+        final SharedStateDiscreteSampler dummySampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(dummyRng, probabilities);
         // This will throw if the table is incomplete as it hits the upper limit
         dummySampler.sample();
 
         // Do a test of the actual sampler
-        final MarsagliaTsangWangDiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng, probabilities);
+        final SharedStateDiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, probabilities);
 
         final int numberOfSamples = 10000;
         final long[] samples = new long[probabilities.length];
@@ -300,7 +300,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final double mean = 1025;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
     }
 
     /**
@@ -311,7 +311,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final double mean = 0;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
     }
 
     /**
@@ -322,7 +322,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final double mean = 1024;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
     }
 
     /**
@@ -333,7 +333,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
     public void testCreatePoissonDistributionWithSmallMean() {
         final UniformRandomProvider rng = new FixedRNG();
         final double mean = 0.25;
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
         // This will throw if the table does not sum to 2^30
         sampler.sample();
     }
@@ -347,7 +347,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
     public void testCreatePoissonDistributionWithMediumMean() {
         final UniformRandomProvider rng = new FixedRNG();
         final double mean = 21.4;
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
         // This will throw if the table does not sum to 2^30
         sampler.sample();
     }
@@ -361,7 +361,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final int trials = -1;
         final double p = 0.5;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
     }
 
     /**
@@ -373,7 +373,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final int trials = 1 << 16; // 2^16
         final double p = 0.5;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
     }
 
     /**
@@ -385,7 +385,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final int trials = 1;
         final double p = -0.5;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
     }
 
     /**
@@ -397,7 +397,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final int trials = 1;
         final double p = 1.5;
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
     }
 
     /**
@@ -422,7 +422,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         Assert.assertEquals("Invalid test set-up for p(0)", 0, getBinomialP0(trials + 1, p), 0);
 
         // This will throw if the table does not sum to 2^30
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
         sampler.sample();
     }
 
@@ -439,7 +439,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         // Validate set-up
         Assert.assertEquals("Invalid test set-up for p(0)", 0, getBinomialP0(trials, p), 0);
         @SuppressWarnings("unused")
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
     }
 
     /**
@@ -482,7 +482,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         Assert.assertEquals("Invalid test set-up for p(0)", Double.MIN_VALUE, getBinomialP0(trials, p), 0);
         Assert.assertEquals("Invalid test set-up for p(0)", 0, getBinomialP0(trials, Math.nextAfter(p, 1)), 0);
 
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
         // This will throw if the table does not sum to 2^30
         sampler.sample();
     }
@@ -506,7 +506,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final int trials = 1000000;
         final double p = 0;
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
         for (int i = 0; i < 5; i++) {
             Assert.assertEquals(0, sampler.sample());
         }
@@ -523,7 +523,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final int trials = 1000000;
         final double p = 1;
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
         for (int i = 0; i < 5; i++) {
             Assert.assertEquals(trials, sampler.sample());
         }
@@ -541,7 +541,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final int trials = 65000;
         final double p = 0.01;
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
         // This will throw if the table does not sum to 2^30
         sampler.sample();
     }
@@ -555,7 +555,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng = new FixedRNG();
         final int trials = 10;
         final double p = 0.5;
-        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+        final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
         // This will throw if the table does not sum to 2^30
         sampler.sample();
     }
@@ -570,8 +570,8 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final int trials = 10;
         final double p1 = 0.4;
         final double p2 = 1 - p1;
-        final DiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p1);
-        final DiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p2);
+        final DiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p1);
+        final DiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p2);
         Assert.assertEquals(sampler1.toString(), sampler2.toString());
     }
 
@@ -610,8 +610,8 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         double[] probabilities = createProbabilities(offset, prob);
-        final MarsagliaTsangWangDiscreteSampler sampler1 =
-                MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng1, probabilities);
+        final SharedStateDiscreteSampler sampler1 =
+                MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng1, probabilities);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
@@ -643,8 +643,8 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
     private static void testSharedStateSampler(int trials, double probabilityOfSuccess) {
         final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
-        final MarsagliaTsangWangDiscreteSampler sampler1 =
-                MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng1, trials, probabilityOfSuccess);
+        final SharedStateDiscreteSampler sampler1 =
+                MarsagliaTsangWangDiscreteSampler.Binomial.of(rng1, trials, probabilityOfSuccess);
         final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
diff --git a/src/main/resources/pmd/pmd-ruleset.xml b/src/main/resources/pmd/pmd-ruleset.xml
index 39d64db..40c1312 100644
--- a/src/main/resources/pmd/pmd-ruleset.xml
+++ b/src/main/resources/pmd/pmd-ruleset.xml
@@ -74,7 +74,8 @@
     <properties>
       <!-- Array is generated internally in this case. -->
       <property name="violationSuppressXPath"
-      value="//ClassOrInterfaceDeclaration[@Image='PoissonSamplerCache' or @Image='AliasMethodDiscreteSampler']"/>
+        value="//ClassOrInterfaceDeclaration[@Image='PoissonSamplerCache' or @Image='AliasMethodDiscreteSampler'
+          or @Image='GuideTableDiscreteSampler']"/>
     </properties>
   </rule>
 


[commons-rng] 02/08: RNG-110: Make distributions samplers implement SharedStateXXXSampler.

Posted by ah...@apache.org.
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

commit 4b43e805fd856296fd1de450c62a9ce8fb3be011
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jul 19 21:21:57 2019 +0100

    RNG-110: Make distributions samplers implement SharedStateXXXSampler.
    
    XXX is Discrete or Continuous.
    
    All withUniformRandomProvider implementations updated to return an
    instance of the appropriate interface.
---
 .../AhrensDieterExponentialSampler.java            |  5 ++-
 .../AhrensDieterMarsagliaTsangGammaSampler.java    | 15 ++++-----
 .../distribution/AliasMethodDiscreteSampler.java   |  9 +++--
 .../BoxMullerNormalizedGaussianSampler.java        |  5 ++-
 .../sampling/distribution/ChengBetaSampler.java    |  5 ++-
 .../distribution/ContinuousUniformSampler.java     |  3 +-
 .../distribution/DiscreteUniformSampler.java       | 15 ++++-----
 .../rng/sampling/distribution/GaussianSampler.java |  4 +--
 .../sampling/distribution/GeometricSampler.java    | 20 +++++------
 .../distribution/GuideTableDiscreteSampler.java    |  5 ++-
 .../InverseTransformContinuousSampler.java         |  5 ++-
 .../InverseTransformDiscreteSampler.java           |  5 ++-
 .../InverseTransformParetoSampler.java             |  5 ++-
 .../distribution/KempSmallMeanPoissonSampler.java  |  5 ++-
 .../distribution/LargeMeanPoissonSampler.java      | 39 +++++++++++++---------
 .../sampling/distribution/LogNormalSampler.java    |  4 +--
 .../MarsagliaNormalizedGaussianSampler.java        |  5 ++-
 .../MarsagliaTsangWangDiscreteSampler.java         | 17 +++++-----
 .../rng/sampling/distribution/PoissonSampler.java  | 18 +++-------
 .../RejectionInversionZipfSampler.java             |  5 ++-
 .../distribution/SmallMeanPoissonSampler.java      |  5 ++-
 .../ZigguratNormalizedGaussianSampler.java         |  5 ++-
 .../AhrensDieterExponentialSamplerTest.java        |  2 +-
 ...AhrensDieterMarsagliaTsangGammaSamplerTest.java |  2 +-
 .../AliasMethodDiscreteSamplerTest.java            |  2 +-
 .../BoxMullerNormalisedGaussianSamplerTest.java    |  2 +-
 .../distribution/ChengBetaSamplerTest.java         |  2 +-
 .../distribution/ContinuousUniformSamplerTest.java |  2 +-
 .../distribution/DiscreteUniformSamplerTest.java   |  2 +-
 .../sampling/distribution/GaussianSamplerTest.java |  2 +-
 .../distribution/GeometricSamplerTest.java         |  2 +-
 .../GuideTableDiscreteSamplerTest.java             |  2 +-
 .../InverseTransformContinuousSamplerTest.java     |  2 +-
 .../InverseTransformDiscreteSamplerTest.java       |  2 +-
 .../InverseTransformParetoSamplerTest.java         |  2 +-
 .../KempSmallMeanPoissonSamplerTest.java           |  2 +-
 .../distribution/LargeMeanPoissonSamplerTest.java  |  2 +-
 .../distribution/LogNormalSamplerTest.java         |  2 +-
 .../MarsagliaNormalisedGaussianSamplerTest.java    |  2 +-
 .../MarsagliaTsangWangDiscreteSamplerTest.java     |  4 +--
 .../sampling/distribution/PoissonSamplerTest.java  |  2 +-
 .../RejectionInversionZipfSamplerTest.java         |  2 +-
 .../distribution/SmallMeanPoissonSamplerTest.java  |  2 +-
 .../ZigguratNormalizedGaussianSamplerTest.java     |  2 +-
 44 files changed, 115 insertions(+), 135 deletions(-)

diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
index 2d93c28..0340a45 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampling from an <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">exponential distribution</a>.
@@ -28,7 +27,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class AhrensDieterExponentialSampler
     extends SamplerBase
-    implements ContinuousSampler, SharedStateSampler<AhrensDieterExponentialSampler> {
+    implements SharedStateContinuousSampler {
     /**
      * Table containing the constants
      * \( q_i = sum_{j=1}^i (\ln 2)^j / j! = \ln 2 + (\ln 2)^2 / 2 + ... + (\ln 2)^i / i! \)
@@ -139,7 +138,7 @@ public class AhrensDieterExponentialSampler
 
     /** {@inheritDoc} */
     @Override
-    public AhrensDieterExponentialSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new AhrensDieterExponentialSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
index 08901ff..006581d 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampling from the <a href="http://mathworld.wolfram.com/GammaDistribution.html">Gamma distribution</a>.
@@ -53,15 +52,15 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class AhrensDieterMarsagliaTsangGammaSampler
     extends SamplerBase
-    implements ContinuousSampler, SharedStateSampler<AhrensDieterMarsagliaTsangGammaSampler> {
+    implements SharedStateContinuousSampler {
     /** The appropriate gamma sampler for the parameters. */
-    private final ContinuousSampler delegate;
+    private final SharedStateContinuousSampler delegate;
 
     /**
      * Base class for a sampler from the Gamma distribution.
      */
     private abstract static class BaseGammaSampler
-        implements ContinuousSampler, SharedStateSampler<ContinuousSampler> {
+        implements SharedStateContinuousSampler {
 
         /** Underlying source of randomness. */
         protected final UniformRandomProvider rng;
@@ -183,7 +182,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
         }
 
         @Override
-        public ContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new AhrensDieterGammaSampler(rng, this);
         }
     }
@@ -263,7 +262,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
         }
 
         @Override
-        public ContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new MarsagliaTsangGammaSampler(rng, this);
         }
     }
@@ -291,7 +290,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
     private AhrensDieterMarsagliaTsangGammaSampler(UniformRandomProvider rng,
                                                    AhrensDieterMarsagliaTsangGammaSampler source) {
         super(null);
-        delegate = ((SharedStateSampler<ContinuousSampler>)(source.delegate)).withUniformRandomProvider(rng);
+        delegate = source.delegate.withUniformRandomProvider(rng);
     }
 
     /** {@inheritDoc} */
@@ -308,7 +307,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler
 
     /** {@inheritDoc} */
     @Override
-    public AhrensDieterMarsagliaTsangGammaSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new AhrensDieterMarsagliaTsangGammaSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
index c5018e0..2ad3593 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 import java.util.Arrays;
 
@@ -63,15 +62,15 @@ import java.util.Arrays;
  * <p>Larger table sizes that are a power of 2 will benefit from fast algorithms for
  * {@link UniformRandomProvider#nextInt(int)} that exploit the power of 2.</p>
  *
- * @since 1.3
  * @see <a href="https://en.wikipedia.org/wiki/Alias_method">Alias Method</a>
  * @see <a href="http://www.keithschwarz.com/darts-dice-coins/">Darts, Dice, and Coins:
  * Sampling from a Discrete Distribution by Keith Schwartz</a>
  * @see <a href="https://ieeexplore.ieee.org/document/92917">Vose (1991) IEEE Transactions
  * on Software Engineering 17, 972-975.</a>
+ * @since 1.3
  */
 public class AliasMethodDiscreteSampler
-    implements DiscreteSampler, SharedStateSampler<AliasMethodDiscreteSampler> {
+    implements SharedStateDiscreteSampler {
     /**
      * The default alpha factor for zero-padding an input probability table. The default
      * value will pad the probabilities by to the next power-of-2.
@@ -213,7 +212,7 @@ public class AliasMethodDiscreteSampler
 
         /** {@inheritDoc} */
         @Override
-        public SmallTableAliasMethodDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new SmallTableAliasMethodDiscreteSampler(rng, probability, alias);
         }
     }
@@ -280,7 +279,7 @@ public class AliasMethodDiscreteSampler
 
     /** {@inheritDoc} */
     @Override
-    public AliasMethodDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new AliasMethodDiscreteSampler(rng, probability, alias);
     }
 
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java
index 5910060..d707670 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * <a href="https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform">
@@ -29,7 +28,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * @since 1.1
  */
 public class BoxMullerNormalizedGaussianSampler
-    implements NormalizedGaussianSampler, SharedStateSampler<BoxMullerNormalizedGaussianSampler> {
+    implements NormalizedGaussianSampler, SharedStateContinuousSampler {
     /** Next gaussian. */
     private double nextGaussian = Double.NaN;
     /** Underlying source of randomness. */
@@ -79,7 +78,7 @@ public class BoxMullerNormalizedGaussianSampler
 
     /** {@inheritDoc} */
     @Override
-    public BoxMullerNormalizedGaussianSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new BoxMullerNormalizedGaussianSampler(rng);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java
index 682ae9c..6fe83e7 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ChengBetaSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Utility class implementing Cheng's algorithms for beta distribution sampling.
@@ -36,7 +35,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class ChengBetaSampler
     extends SamplerBase
-    implements ContinuousSampler, SharedStateSampler<ChengBetaSampler> {
+    implements SharedStateContinuousSampler {
     /** 1/2. */
     private static final double ONE_HALF = 1d / 2;
     /** 1/4. */
@@ -105,7 +104,7 @@ public class ChengBetaSampler
 
     /** {@inheritDoc} */
     @Override
-    public ChengBetaSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new ChengBetaSampler(rng, this);
     }
 
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
index 2a3a9ea..43a7ec9 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampling from a uniform distribution.
@@ -28,7 +27,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class ContinuousUniformSampler
     extends SamplerBase
-    implements ContinuousSampler, SharedStateSampler<ContinuousUniformSampler> {
+    implements SharedStateContinuousSampler {
     /** Lower bound. */
     private final double lo;
     /** Higher bound. */
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
index 1809c4c..bc1830c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
@@ -18,7 +18,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Discrete uniform distribution sampler.
@@ -31,16 +30,16 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class DiscreteUniformSampler
     extends SamplerBase
-    implements DiscreteSampler, SharedStateSampler<DiscreteUniformSampler> {
+    implements SharedStateDiscreteSampler {
 
     /** The appropriate uniform sampler for the parameters. */
-    private final DiscreteSampler delegate;
+    private final SharedStateDiscreteSampler delegate;
 
     /**
      * Base class for a sampler from a discrete uniform distribution.
      */
     private abstract static class AbstractDiscreteUniformSampler
-        implements DiscreteSampler, SharedStateSampler<DiscreteSampler> {
+        implements SharedStateDiscreteSampler {
 
         /** Underlying source of randomness. */
         protected final UniformRandomProvider rng;
@@ -91,7 +90,7 @@ public class DiscreteUniformSampler
         }
 
         @Override
-        public DiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new SmallRangeDiscreteUniformSampler(rng, lower, range);
         }
     }
@@ -134,7 +133,7 @@ public class DiscreteUniformSampler
         }
 
         @Override
-        public DiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new LargeRangeDiscreteUniformSampler(rng, lower, upper);
         }
     }
@@ -170,7 +169,7 @@ public class DiscreteUniformSampler
     private DiscreteUniformSampler(UniformRandomProvider rng,
                                    DiscreteUniformSampler source) {
         super(null);
-        delegate = ((SharedStateSampler<DiscreteSampler>)(source.delegate)).withUniformRandomProvider(rng);
+        delegate = source.delegate.withUniformRandomProvider(rng);
     }
 
     /** {@inheritDoc} */
@@ -187,7 +186,7 @@ public class DiscreteUniformSampler
 
     /** {@inheritDoc} */
     @Override
-    public DiscreteUniformSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new DiscreteUniformSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java
index 8362387..91c29e7 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GaussianSampler.java
@@ -25,7 +25,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  *
  * @since 1.1
  */
-public class GaussianSampler implements ContinuousSampler, SharedStateSampler<GaussianSampler> {
+public class GaussianSampler implements SharedStateContinuousSampler {
     /** Mean. */
     private final double mean;
     /** standardDeviation. */
@@ -85,7 +85,7 @@ public class GaussianSampler implements ContinuousSampler, SharedStateSampler<Ga
      * sharing state.
      */
     @Override
-    public GaussianSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new GaussianSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
index f1e3470..a65a46e 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampling from a <a href="https://en.wikipedia.org/wiki/Geometric_distribution">geometric
@@ -46,15 +45,15 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  *
  * @since 1.3
  */
-public class GeometricSampler implements DiscreteSampler, SharedStateSampler<GeometricSampler> {
+public class GeometricSampler implements SharedStateDiscreteSampler {
     /** The appropriate geometric sampler for the parameters. */
-    private final DiscreteSampler delegate;
+    private final SharedStateDiscreteSampler delegate;
 
     /**
      * Sample from the geometric distribution when the probability of success is 1.
      */
     private static class GeometricP1Sampler
-        implements DiscreteSampler, SharedStateSampler<DiscreteSampler> {
+        implements SharedStateDiscreteSampler {
         /** The single instance. */
         static final GeometricP1Sampler INSTANCE = new GeometricP1Sampler();
 
@@ -70,7 +69,7 @@ public class GeometricSampler implements DiscreteSampler, SharedStateSampler<Geo
         }
 
         @Override
-        public DiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             // No requirement for a new instance
             return this;
         }
@@ -80,11 +79,11 @@ public class GeometricSampler implements DiscreteSampler, SharedStateSampler<Geo
      * Sample from the geometric distribution by using a related exponential distribution.
      */
     private static class GeometricExponentialSampler
-        implements DiscreteSampler, SharedStateSampler<DiscreteSampler> {
+        implements SharedStateDiscreteSampler {
         /** Underlying source of randomness. Used only for the {@link #toString()} method. */
         private final UniformRandomProvider rng;
         /** The related exponential sampler for the geometric distribution. */
-        private final AhrensDieterExponentialSampler exponentialSampler;
+        private final SharedStateContinuousSampler exponentialSampler;
 
         /**
          * @param rng Generator of uniformly distributed random numbers
@@ -128,7 +127,7 @@ public class GeometricSampler implements DiscreteSampler, SharedStateSampler<Geo
         }
 
         @Override
-        public DiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new GeometricExponentialSampler(rng, this);
         }
     }
@@ -158,9 +157,8 @@ public class GeometricSampler implements DiscreteSampler, SharedStateSampler<Geo
      * @param rng Generator of uniformly distributed random numbers
      * @param source Source to copy.
      */
-    @SuppressWarnings("unchecked")
     private GeometricSampler(UniformRandomProvider rng, GeometricSampler source) {
-        delegate = ((SharedStateSampler<DiscreteSampler>)(source.delegate)).withUniformRandomProvider(rng);
+        delegate = source.delegate.withUniformRandomProvider(rng);
     }
 
     /**
@@ -182,7 +180,7 @@ public class GeometricSampler implements DiscreteSampler, SharedStateSampler<Geo
 
     /** {@inheritDoc} */
     @Override
-    public GeometricSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new GeometricSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
index cb02cbb..0ca3315 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Compute a sample from a discrete probability distribution. The cumulative probability
@@ -41,7 +40,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * @since 1.3
  */
 public class GuideTableDiscreteSampler
-    implements DiscreteSampler, SharedStateSampler<GuideTableDiscreteSampler> {
+    implements SharedStateDiscreteSampler {
     /** The default value for {@code alpha}. */
     private static final double DEFAULT_ALPHA = 1.0;
     /** Underlying source of randomness. */
@@ -213,7 +212,7 @@ public class GuideTableDiscreteSampler
 
     /** {@inheritDoc} */
     @Override
-    public GuideTableDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new GuideTableDiscreteSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java
index f60e1d0..4401d9e 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Distribution sampler that uses the
@@ -58,7 +57,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class InverseTransformContinuousSampler
     extends SamplerBase
-    implements ContinuousSampler, SharedStateSampler<InverseTransformContinuousSampler> {
+    implements SharedStateContinuousSampler {
     /** Inverse cumulative probability function. */
     private final ContinuousInverseCumulativeProbabilityFunction function;
     /** Underlying source of randomness. */
@@ -94,7 +93,7 @@ public class InverseTransformContinuousSampler
      * must be suitable for concurrent use to ensure thread safety.</p>
      */
     @Override
-    public InverseTransformContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new InverseTransformContinuousSampler(rng, function);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java
index e10e2c4..6067a8c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Distribution sampler that uses the
@@ -58,7 +57,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class InverseTransformDiscreteSampler
     extends SamplerBase
-    implements DiscreteSampler, SharedStateSampler<InverseTransformDiscreteSampler> {
+    implements SharedStateDiscreteSampler {
     /** Inverse cumulative probability function. */
     private final DiscreteInverseCumulativeProbabilityFunction function;
     /** Underlying source of randomness. */
@@ -94,7 +93,7 @@ public class InverseTransformDiscreteSampler
      * must be suitable for concurrent use to ensure thread safety.</p>
      */
     @Override
-    public InverseTransformDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new InverseTransformDiscreteSampler(rng, function);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java
index 4068b49..a7b6d7c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampling from a <a href="https://en.wikipedia.org/wiki/Pareto_distribution">Pareto distribution</a>.
@@ -28,7 +27,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class InverseTransformParetoSampler
     extends SamplerBase
-    implements ContinuousSampler, SharedStateSampler<InverseTransformParetoSampler> {
+    implements SharedStateContinuousSampler {
     /** Scale. */
     private final double scale;
     /** 1 / Shape. */
@@ -83,7 +82,7 @@ public class InverseTransformParetoSampler
 
     /** {@inheritDoc} */
     @Override
-    public InverseTransformParetoSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new InverseTransformParetoSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
index b3203d3..7c4a09c 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampler for the <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson
@@ -47,7 +46,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * 249-253</a>
  */
 public class KempSmallMeanPoissonSampler
-    implements DiscreteSampler, SharedStateSampler<KempSmallMeanPoissonSampler> {
+    implements SharedStateDiscreteSampler {
     /** Underlying source of randomness. */
     private final UniformRandomProvider rng;
     /**
@@ -129,7 +128,7 @@ public class KempSmallMeanPoissonSampler
 
     /** {@inheritDoc} */
     @Override
-    public KempSmallMeanPoissonSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new KempSmallMeanPoissonSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
index ce8d9ae..2a68e32 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 import org.apache.commons.rng.sampling.distribution.InternalUtils.FactorialLog;
 
 /**
@@ -45,13 +44,26 @@ import org.apache.commons.rng.sampling.distribution.InternalUtils.FactorialLog;
  * @since 1.1
  */
 public class LargeMeanPoissonSampler
-    implements DiscreteSampler, SharedStateSampler<LargeMeanPoissonSampler> {
+    implements SharedStateDiscreteSampler {
     /** Upper bound to avoid truncation. */
     private static final double MAX_MEAN = 0.5 * Integer.MAX_VALUE;
     /** Class to compute {@code log(n!)}. This has no cached values. */
     private static final InternalUtils.FactorialLog NO_CACHE_FACTORIAL_LOG;
     /** Used when there is no requirement for a small mean Poisson sampler. */
-    private static final KempSmallMeanPoissonSampler NO_SMALL_MEAN_POISSON_SAMPLER = null;
+    private static final SharedStateDiscreteSampler NO_SMALL_MEAN_POISSON_SAMPLER =
+        new SharedStateDiscreteSampler() {
+            @Override
+            public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+                // No requirement for RNG
+                return this;
+            }
+
+            @Override
+            public int sample() {
+                // No Poisson sample
+                return 0;
+            }
+        };
 
     static {
         // Create without a cache.
@@ -61,9 +73,9 @@ public class LargeMeanPoissonSampler
     /** Underlying source of randomness. */
     private final UniformRandomProvider rng;
     /** Exponential. */
-    private final AhrensDieterExponentialSampler exponential;
+    private final SharedStateContinuousSampler exponential;
     /** Gaussian. */
-    private final ContinuousSampler gaussian;
+    private final SharedStateContinuousSampler gaussian;
     /** Local class to compute {@code log(n!)}. This may have cached values. */
     private final InternalUtils.FactorialLog factorialLog;
 
@@ -101,7 +113,7 @@ public class LargeMeanPoissonSampler
     private final double c1;
 
     /** The internal Poisson sampler for the lambda fraction. */
-    private final KempSmallMeanPoissonSampler smallMeanPoissonSampler;
+    private final SharedStateDiscreteSampler smallMeanPoissonSampler;
 
     /**
      * @param rng Generator of uniformly distributed random numbers.
@@ -195,8 +207,7 @@ public class LargeMeanPoissonSampler
                                     LargeMeanPoissonSampler source) {
         this.rng = rng;
 
-        // The Gaussian sampler has no shared state
-        gaussian = new ZigguratNormalizedGaussianSampler(rng);
+        gaussian = source.gaussian.withUniformRandomProvider(rng);
         exponential = source.exponential.withUniformRandomProvider(rng);
         // Reuse the cache
         factorialLog = source.factorialLog;
@@ -212,18 +223,14 @@ public class LargeMeanPoissonSampler
         c1 = source.c1;
 
         // Share the state of the small sampler
-        smallMeanPoissonSampler = source.smallMeanPoissonSampler == null ?
-            NO_SMALL_MEAN_POISSON_SAMPLER : // Not used.
-            source.smallMeanPoissonSampler.withUniformRandomProvider(rng);
+        smallMeanPoissonSampler = source.smallMeanPoissonSampler.withUniformRandomProvider(rng);
     }
 
     /** {@inheritDoc} */
     @Override
     public int sample() {
-
-        final int y2 = (smallMeanPoissonSampler == null) ?
-            0 : // No lambda fraction
-            smallMeanPoissonSampler.sample();
+        // This will never be null. It may be a no-op delegate that returns zero.
+        final int y2 = smallMeanPoissonSampler.sample();
 
         double x;
         double y;
@@ -295,7 +302,7 @@ public class LargeMeanPoissonSampler
 
     /** {@inheritDoc} */
     @Override
-    public LargeMeanPoissonSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new LargeMeanPoissonSampler(rng, this);
     }
 
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java
index 55dab9e..9bf9568 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LogNormalSampler.java
@@ -24,7 +24,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  *
  * @since 1.1
  */
-public class LogNormalSampler implements ContinuousSampler, SharedStateSampler<LogNormalSampler> {
+public class LogNormalSampler implements SharedStateContinuousSampler {
     /** Scale. */
     private final double scale;
     /** Shape. */
@@ -86,7 +86,7 @@ public class LogNormalSampler implements ContinuousSampler, SharedStateSampler<L
      * sharing state.
      */
     @Override
-    public LogNormalSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new LogNormalSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java
index 421288a..7068052 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * <a href="https://en.wikipedia.org/wiki/Marsaglia_polar_method">
@@ -31,7 +30,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * @since 1.1
  */
 public class MarsagliaNormalizedGaussianSampler
-    implements NormalizedGaussianSampler, SharedStateSampler<MarsagliaNormalizedGaussianSampler> {
+    implements NormalizedGaussianSampler, SharedStateContinuousSampler {
     /** Next gaussian. */
     private double nextGaussian = Double.NaN;
     /** Underlying source of randomness. */
@@ -88,7 +87,7 @@ public class MarsagliaNormalizedGaussianSampler
 
     /** {@inheritDoc} */
     @Override
-    public MarsagliaNormalizedGaussianSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new MarsagliaNormalizedGaussianSampler(rng);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
index c7528f4..32bc12e 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampler for a discrete distribution using an optimised look-up table.
@@ -51,7 +50,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * 11, Issue 3</a>
  */
 public abstract class MarsagliaTsangWangDiscreteSampler
-    implements DiscreteSampler, SharedStateSampler<MarsagliaTsangWangDiscreteSampler> {
+    implements SharedStateDiscreteSampler {
     /** The value 2<sup>8</sup> as an {@code int}. */
     private static final int INT_8 = 1 << 8;
     /** The value 2<sup>16</sup> as an {@code int}. */
@@ -248,7 +247,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         }
 
         @Override
-        public MarsagliaTsangWangDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new MarsagliaTsangWangBase64Int8DiscreteSampler(rng, this);
         }
     }
@@ -388,7 +387,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         }
 
         @Override
-        public MarsagliaTsangWangDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new MarsagliaTsangWangBase64Int16DiscreteSampler(rng, this);
         }
     }
@@ -524,7 +523,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         }
 
         @Override
-        public MarsagliaTsangWangDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new MarsagliaTsangWangBase64Int32DiscreteSampler(rng, this);
         }
     }
@@ -557,7 +556,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         }
 
         @Override
-        public MarsagliaTsangWangDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             // No shared state
             return this;
         }
@@ -576,14 +575,14 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         /** The number of trials. */
         private final int trials;
         /** The Binomial distribution sampler. */
-        private final MarsagliaTsangWangDiscreteSampler sampler;
+        private final SharedStateDiscreteSampler sampler;
 
         /**
          * @param trials Number of trials.
          * @param sampler Binomial distribution sampler.
          */
         MarsagliaTsangWangInversionBinomialSampler(int trials,
-                                                   MarsagliaTsangWangDiscreteSampler sampler) {
+                                                   SharedStateDiscreteSampler sampler) {
             super(null, BINOMIAL_NAME);
             this.trials = trials;
             this.sampler = sampler;
@@ -600,7 +599,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
         }
 
         @Override
-        public MarsagliaTsangWangDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+        public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
             return new MarsagliaTsangWangInversionBinomialSampler(this.trials,
                 this.sampler.withUniformRandomProvider(rng));
         }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
index 804a9e3..36cc04b 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/PoissonSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampler for the <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson distribution</a>.
@@ -52,7 +51,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class PoissonSampler
     extends SamplerBase
-    implements DiscreteSampler, SharedStateSampler<PoissonSampler> {
+    implements SharedStateDiscreteSampler {
 
     /**
      * Value for switching sampling algorithm.
@@ -61,7 +60,7 @@ public class PoissonSampler
      */
     static final double PIVOT = 40;
     /** The internal Poisson sampler. */
-    private final DiscreteSampler poissonSamplerDelegate;
+    private final SharedStateDiscreteSampler poissonSamplerDelegate;
 
     /**
      * @param rng Generator of uniformly distributed random numbers.
@@ -85,16 +84,9 @@ public class PoissonSampler
      * @param source Source to copy.
      */
     private PoissonSampler(UniformRandomProvider rng,
-            PoissonSampler source) {
+                           PoissonSampler source) {
         super(null);
-
-        if (source.poissonSamplerDelegate instanceof SmallMeanPoissonSampler) {
-            poissonSamplerDelegate =
-                ((SmallMeanPoissonSampler)source.poissonSamplerDelegate).withUniformRandomProvider(rng);
-        } else {
-            poissonSamplerDelegate =
-                ((LargeMeanPoissonSampler)source.poissonSamplerDelegate).withUniformRandomProvider(rng);
-        }
+        poissonSamplerDelegate = source.poissonSamplerDelegate.withUniformRandomProvider(rng);
     }
 
     /** {@inheritDoc} */
@@ -111,7 +103,7 @@ public class PoissonSampler
 
     /** {@inheritDoc} */
     @Override
-    public PoissonSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new PoissonSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java
index 112f3fe..fb8cb9d 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSampler.java
@@ -18,7 +18,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Implementation of the <a href="https://en.wikipedia.org/wiki/Zipf's_law">Zipf distribution</a>.
@@ -29,7 +28,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  */
 public class RejectionInversionZipfSampler
     extends SamplerBase
-    implements DiscreteSampler, SharedStateSampler<RejectionInversionZipfSampler> {
+    implements SharedStateDiscreteSampler {
     /** Threshold below which Taylor series will be used. */
     private static final double TAYLOR_THRESHOLD = 1e-8;
     /** 1/2. */
@@ -195,7 +194,7 @@ public class RejectionInversionZipfSampler
 
     /** {@inheritDoc} */
     @Override
-    public RejectionInversionZipfSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new RejectionInversionZipfSampler(rng, this);
     }
 
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java
index e6a9722..331b6b0 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.java
@@ -17,7 +17,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * Sampler for the <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson distribution</a>.
@@ -43,7 +42,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * @since 1.1
  */
 public class SmallMeanPoissonSampler
-    implements DiscreteSampler, SharedStateSampler<SmallMeanPoissonSampler> {
+    implements SharedStateDiscreteSampler {
     /**
      * Pre-compute {@code Math.exp(-mean)}.
      * Note: This is the probability of the Poisson sample {@code P(n=0)}.
@@ -111,7 +110,7 @@ public class SmallMeanPoissonSampler
 
     /** {@inheritDoc} */
     @Override
-    public SmallMeanPoissonSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new SmallMeanPoissonSampler(rng, this);
     }
 }
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java
index a1ba383..6b8588a 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.java
@@ -18,7 +18,6 @@
 package org.apache.commons.rng.sampling.distribution;
 
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.sampling.SharedStateSampler;
 
 /**
  * <a href="https://en.wikipedia.org/wiki/Ziggurat_algorithm">
@@ -39,7 +38,7 @@ import org.apache.commons.rng.sampling.SharedStateSampler;
  * @since 1.1
  */
 public class ZigguratNormalizedGaussianSampler
-    implements NormalizedGaussianSampler, SharedStateSampler<ZigguratNormalizedGaussianSampler> {
+    implements NormalizedGaussianSampler, SharedStateContinuousSampler {
     /** Start of tail. */
     private static final double R = 3.442619855899;
     /** Inverse of R. */
@@ -164,7 +163,7 @@ public class ZigguratNormalizedGaussianSampler
 
     /** {@inheritDoc} */
     @Override
-    public ZigguratNormalizedGaussianSampler withUniformRandomProvider(UniformRandomProvider rng) {
+    public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
         return new ZigguratNormalizedGaussianSampler(rng);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java
index 739a178..6fe5139 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSamplerTest.java
@@ -49,7 +49,7 @@ public class AhrensDieterExponentialSamplerTest {
         final double mean = 1.23;
         final AhrensDieterExponentialSampler sampler1 =
             new AhrensDieterExponentialSampler(rng1, mean);
-        final AhrensDieterExponentialSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java
index 11ba878..27a4198 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSamplerTest.java
@@ -81,7 +81,7 @@ public class AhrensDieterMarsagliaTsangGammaSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final AhrensDieterMarsagliaTsangGammaSampler sampler1 =
             new AhrensDieterMarsagliaTsangGammaSampler(rng1, alpha, theta);
-        final AhrensDieterMarsagliaTsangGammaSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
index e162f19..e5f9108 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
@@ -277,7 +277,7 @@ public class AliasMethodDiscreteSamplerTest {
         // Use negative alpha to disable padding
         final AliasMethodDiscreteSampler sampler1 =
             AliasMethodDiscreteSampler.create(rng1, probabilities, -1);
-        final AliasMethodDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java
index 191cbc5..6e21e62 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/BoxMullerNormalisedGaussianSamplerTest.java
@@ -34,7 +34,7 @@ public class BoxMullerNormalisedGaussianSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final BoxMullerNormalizedGaussianSampler sampler1 =
             new BoxMullerNormalizedGaussianSampler(rng1);
-        final BoxMullerNormalizedGaussianSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java
index 24325b0..cb630aa 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ChengBetaSamplerTest.java
@@ -65,7 +65,7 @@ public class ChengBetaSamplerTest {
         final double beta = 4.56;
         final ChengBetaSampler sampler1 =
             new ChengBetaSampler(rng1, alpha, beta);
-        final ChengBetaSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
index 06bf53f..36b9eba 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
@@ -60,7 +60,7 @@ public class ContinuousUniformSamplerTest {
         final double high = 4.56;
         final ContinuousUniformSampler sampler1 =
             new ContinuousUniformSampler(rng1, low, high);
-        final ContinuousUniformSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
index 3a0a34c..9b76e7a 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
@@ -64,7 +64,7 @@ public class DiscreteUniformSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final DiscreteUniformSampler sampler1 =
             new DiscreteUniformSampler(rng1, lower, upper);
-        final DiscreteUniformSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java
index f4afb63..28ca3c0 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GaussianSamplerTest.java
@@ -54,7 +54,7 @@ public class GaussianSamplerTest {
         final double standardDeviation = 4.56;
         final GaussianSampler sampler1 =
             new GaussianSampler(gauss, mean, standardDeviation);
-        final GaussianSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
index 66eaee4..f384485 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
@@ -140,7 +140,7 @@ public class GeometricSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final GeometricSampler sampler1 =
             new GeometricSampler(rng1, probabilityOfSuccess);
-        final GeometricSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
index ec89352..e38b3db 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
@@ -246,7 +246,7 @@ public class GuideTableDiscreteSamplerTest {
         final double[] probabilities = {0.1, 0, 0.2, 0.3, 0.1, 0.3, 0};
         final GuideTableDiscreteSampler sampler1 =
             new GuideTableDiscreteSampler(rng1, probabilities);
-        final GuideTableDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java
index 6ddbb07..99938cf 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformContinuousSamplerTest.java
@@ -41,7 +41,7 @@ public class InverseTransformContinuousSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final InverseTransformContinuousSampler sampler1 =
             new InverseTransformContinuousSampler(rng1, function);
-        final InverseTransformContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java
index b9d3f89..778ffe5 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformDiscreteSamplerTest.java
@@ -41,7 +41,7 @@ public class InverseTransformDiscreteSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final InverseTransformDiscreteSampler sampler1 =
             new InverseTransformDiscreteSampler(rng1, function);
-        final InverseTransformDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java
index b4314c7..3837d1d 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/InverseTransformParetoSamplerTest.java
@@ -65,7 +65,7 @@ public class InverseTransformParetoSamplerTest {
         final double shape = 4.56;
         final InverseTransformParetoSampler sampler1 =
             new InverseTransformParetoSampler(rng1, scale, shape);
-        final InverseTransformParetoSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
index 04b8e7e..2796ff0 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
@@ -156,7 +156,7 @@ public class KempSmallMeanPoissonSamplerTest {
         final double mean = 1.23;
         final KempSmallMeanPoissonSampler sampler1 =
             new KempSmallMeanPoissonSampler(rng1, mean);
-        final KempSmallMeanPoissonSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java
index 89022d1..9a8c674 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSamplerTest.java
@@ -171,7 +171,7 @@ public class LargeMeanPoissonSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final LargeMeanPoissonSampler sampler1 =
             new LargeMeanPoissonSampler(rng1, mean);
-        final LargeMeanPoissonSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java
index a8ec693..3da6447 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/LogNormalSamplerTest.java
@@ -69,7 +69,7 @@ public class LogNormalSamplerTest {
         final double shape = 4.56;
         final LogNormalSampler sampler1 =
             new LogNormalSampler(gauss, scale, shape);
-        final LogNormalSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java
index 89b7339..1155ce4 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaNormalisedGaussianSamplerTest.java
@@ -34,7 +34,7 @@ public class MarsagliaNormalisedGaussianSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final MarsagliaNormalizedGaussianSampler sampler1 =
             new MarsagliaNormalizedGaussianSampler(rng1);
-        final MarsagliaNormalizedGaussianSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
index 37164f7..5f2dd87 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
@@ -612,7 +612,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         double[] probabilities = createProbabilities(offset, prob);
         final MarsagliaTsangWangDiscreteSampler sampler1 =
                 MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng1, probabilities);
-        final MarsagliaTsangWangDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 
@@ -645,7 +645,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final MarsagliaTsangWangDiscreteSampler sampler1 =
                 MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng1, trials, probabilityOfSuccess);
-        final MarsagliaTsangWangDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java
index 9899b61..362397d 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/PoissonSamplerTest.java
@@ -52,7 +52,7 @@ public class PoissonSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final PoissonSampler sampler1 =
             new PoissonSampler(rng1, mean);
-        final PoissonSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java
index d7e3654..15621da 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/RejectionInversionZipfSamplerTest.java
@@ -65,7 +65,7 @@ public class RejectionInversionZipfSamplerTest {
         final double exponent = 1.23;
         final RejectionInversionZipfSampler sampler1 =
             new RejectionInversionZipfSampler(rng1, numberOfElements, exponent);
-        final RejectionInversionZipfSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
index 8d34fac..f21bf95 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
@@ -89,7 +89,7 @@ public class SmallMeanPoissonSamplerTest {
         final double mean = 1.23;
         final SmallMeanPoissonSampler sampler1 =
             new SmallMeanPoissonSampler(rng1, mean);
-        final SmallMeanPoissonSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
index d4a2e16..1772de4 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
@@ -57,7 +57,7 @@ public class ZigguratNormalizedGaussianSamplerTest {
         final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
         final ZigguratNormalizedGaussianSampler sampler1 =
             new ZigguratNormalizedGaussianSampler(rng1);
-        final ZigguratNormalizedGaussianSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
+        final SharedStateContinuousSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
     }
 }


[commons-rng] 08/08: RNG-110: Track changes.

Posted by ah...@apache.org.
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

commit 63420f65cba6fb75516a76c0edb0d963522a710e
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Wed Jul 31 22:47:12 2019 +0100

    RNG-110: Track changes.
---
 src/changes/changes.xml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index de1b96d..66551c4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -75,6 +75,10 @@ re-run tests that fail, and pass the build if they succeed
 within the allotted number of reruns (the test will be marked
 as 'flaky' in the report).
 ">
+      <action dev="aherbert" type="add" issue="RNG-110">
+        Factory methods for Discrete and Continuous distribution samplers. The factory method
+        can choose the optimal implementation for the distribution parameters.
+      </action>
       <action dev="aherbert" type="add" issue="RNG-84" due-to="Abhishek Singh Dhadwal">
         New Permuted Congruential Generators (PCG) from the PCG family.
         Added the LCG and MCG 32 bit output versions of the XSH-RS and XSH-RR operations,


[commons-rng] 01/08: RNG-110: Add SharedStateDiscreteSampler and SharedStateContinuousSampler

Posted by ah...@apache.org.
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

commit 789dc713ccd9f66dd90cdd4780758ba3ca714d6d
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jul 19 18:16:18 2019 +0100

    RNG-110: Add SharedStateDiscreteSampler and SharedStateContinuousSampler
    
    These interfaces define functionality for all distribution samplers.
---
 .../distribution/SharedStateContinuousSampler.java | 30 ++++++++++++++++++++++
 .../distribution/SharedStateDiscreteSampler.java   | 30 ++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SharedStateContinuousSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SharedStateContinuousSampler.java
new file mode 100644
index 0000000..b1b8bac
--- /dev/null
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SharedStateContinuousSampler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.sampling.distribution;
+
+import org.apache.commons.rng.sampling.SharedStateSampler;
+
+/**
+ * Sampler that generates values of type {@code double} and can create new instances to sample
+ * from the same state with a given source of randomness.
+ *
+ * @since 1.3
+ */
+public interface SharedStateContinuousSampler
+    extends ContinuousSampler, SharedStateSampler<SharedStateContinuousSampler> {
+    // Composite interface
+}
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SharedStateDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SharedStateDiscreteSampler.java
new file mode 100644
index 0000000..fe751bf
--- /dev/null
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/SharedStateDiscreteSampler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.sampling.distribution;
+
+import org.apache.commons.rng.sampling.SharedStateSampler;
+
+/**
+ * Sampler that generates values of type {@code int} and can create new instances to sample
+ * from the same state with a given source of randomness.
+ *
+ * @since 1.3
+ */
+public interface SharedStateDiscreteSampler
+    extends DiscreteSampler, SharedStateSampler<SharedStateDiscreteSampler> {
+    // Composite interface
+}


[commons-rng] 04/08: RNG-110: PMD rule exceptions to allow Sampler factory constructors.

Posted by ah...@apache.org.
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

commit 6da71403f9cb59cf112823715fdc9b57d15084d1
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jul 19 22:32:22 2019 +0100

    RNG-110: PMD rule exceptions to allow Sampler factory constructors.
    
    Samplers can be utility classes without a Utils/Helper suffix.
    
    Samplers can have short method names, e.g. of(...).
---
 src/main/resources/pmd/pmd-ruleset.xml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/main/resources/pmd/pmd-ruleset.xml b/src/main/resources/pmd/pmd-ruleset.xml
index 9b48ba1..39d64db 100644
--- a/src/main/resources/pmd/pmd-ruleset.xml
+++ b/src/main/resources/pmd/pmd-ruleset.xml
@@ -84,11 +84,14 @@
       <property name="violationSuppressXPath"
         value="//ClassOrInterfaceDeclaration[@Image='ListSampler' or @Image='ProviderBuilder'
           or @Image='ThreadLocalRandomSource' or @Image='SeedFactory']"/>
+      <!-- Allow samplers to have only factory constructors -->
+      <property name="utilityClassPattern" value="[A-Z][a-zA-Z0-9]+(Utils?|Helper|Sampler)" />
     </properties>
   </rule>
   <rule ref="category/java/codestyle.xml/ShortMethodName">
     <properties>
-      <property name="violationSuppressXPath" value="//ClassOrInterfaceDeclaration[@Image='RejectionInversionZipfSampler']"/>
+      <!-- Allow samplers to have factory constructor method 'of' -->
+      <property name="violationSuppressXPath" value="//ClassOrInterfaceDeclaration[matches(@Image, '^.*Sampler$')]"/>
     </properties>
   </rule>