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:24 UTC

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

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