View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.rng.sampling.distribution;
18  
19  import java.util.function.Supplier;
20  import org.apache.commons.rng.UniformRandomProvider;
21  import org.apache.commons.rng.core.source64.SplitMix64;
22  import org.apache.commons.rng.sampling.RandomAssert;
23  import org.apache.commons.rng.sampling.distribution.StableSampler.Beta0CMSStableSampler;
24  import org.apache.commons.rng.sampling.distribution.StableSampler.Beta0WeronStableSampler;
25  import org.apache.commons.rng.sampling.distribution.StableSampler.CMSStableSampler;
26  import org.apache.commons.rng.sampling.distribution.StableSampler.SpecialMath;
27  import org.apache.commons.rng.sampling.distribution.StableSampler.WeronStableSampler;
28  import org.apache.commons.rng.simple.RandomSource;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.Test;
31  
32  /**
33   * Tests for the class {@link StableSampler}.
34   *
35   * <p>Note: Samples from the stable distribution are tested in
36   * {@link ContinuousSamplerParametricTest}.
37   *
38   * <p>This contains tests for the assumptions made by the {@link StableSampler} implementation
39   * of the Chambers-Mallows-Stuck (CMS) method as described in
40   * Chambers, Mallows &amp; Stuck (1976) "A Method for Simulating Stable Random Variables".
41   * Journal of the American Statistical Association. 71 (354): 340–344.
42   *
43   * <p>The test class contains copy implementations of the routines in the {@link StableSampler}
44   * to test the algorithms with various parameters. This avoids excess manipulation
45   * of the RNG provided to the stable sampler to test edge cases and also allows
46   * calling the algorithm with values that are eliminated by the sampler (e.g. u=0).
47   *
48   * <p>Some tests of the sampler are performed that manipulate the underlying RNG to create
49   * extreme values for the random deviates. This hits edges cases where the computation has
50   * to be corrected.
51   */
52  class StableSamplerTest {
53      /** pi / 2. */
54      private static final double PI_2 = Math.PI / 2;
55      /** pi / 4. */
56      private static final double PI_4 = Math.PI / 4;
57      /** pi/4 scaled by 2^-53. */
58      private static final double PI_4_SCALED = 0x1.0p-55 * Math.PI;
59      /** The interval between successive values of a uniform variate u.
60       * This is the gap between the 2^53 dyadic rationals in [0, 1). */
61      private static final double DU = 0x1.0p-53;
62      /** The smallest non-zero sample from the ZigguratSampler.Exponential sampler. */
63      private static final double SMALL_W = 6.564735882096453E-19;
64      /** A tail sample from the ZigguratSampler.Exponential after 1 recursions of the sample method.  */
65      private static final double TAIL_W = 7.569274694148063;
66      /** A largest sample from the ZigguratSampler.Exponential after 4 recursions of the sample method.  */
67      private static final double LARGE_W = 4 * TAIL_W;
68      /** The smallest value for alpha where 1 - (1-alpha) = alpha. */
69      private static final double SMALLEST_ALPHA = 1.0 - Math.nextDown(1.0);
70  
71      private static final double VALID_ALPHA = 1.23;
72      private static final double VALID_BETA = 0.23;
73      private static final double VALID_GAMMA = 2.34;
74      private static final double VALID_DELTA = 3.45;
75  
76      @Test
77      void testAlphaZeroThrows() {
78          assertConstructorThrows(0.0, VALID_BETA, VALID_GAMMA, VALID_DELTA);
79      }
80  
81      @Test
82      void testAlphaBelowZeroThrows() {
83          assertConstructorThrows(Math.nextDown(0.0), VALID_BETA, VALID_GAMMA, VALID_DELTA);
84      }
85  
86      @Test
87      void testAlphaTooCloseToZeroThrows() {
88          // The realistic range for alpha is not Double.MIN_VALUE.
89          // The number 1 - alpha must not be 1.
90          // This is valid
91          final UniformRandomProvider rng = new SplitMix64(0L);
92          StableSampler s = StableSampler.of(rng, SMALLEST_ALPHA, VALID_BETA, VALID_GAMMA, VALID_DELTA);
93          Assertions.assertNotNull(s);
94  
95          // Smaller than this is still above zero but 1 - alpha == 1
96          final double alphaTooSmall = SMALLEST_ALPHA / 2;
97          Assertions.assertNotEquals(0.0, alphaTooSmall, "Expected alpha to be positive");
98          Assertions.assertEquals(1.0, 1 - alphaTooSmall, "Expected rounding to 1");
99  
100         // Because alpha is effectively zero this will throw
101         assertConstructorThrows(alphaTooSmall, VALID_BETA, VALID_GAMMA, VALID_DELTA);
102     }
103 
104     @Test
105     void testAlphaAboveTwoThrows() {
106         assertConstructorThrows(Math.nextUp(2.0), VALID_BETA, VALID_GAMMA, VALID_DELTA);
107     }
108 
109     @Test
110     void testAlphaNaNThrows() {
111         assertConstructorThrows(Double.NaN, VALID_BETA, VALID_GAMMA, VALID_DELTA);
112     }
113 
114     @Test
115     void testBetaBelowMinusOneThrows() {
116         assertConstructorThrows(VALID_ALPHA, Math.nextDown(-1.0), VALID_GAMMA, VALID_DELTA);
117     }
118 
119     @Test
120     void testBetaAboveOneThrows() {
121         assertConstructorThrows(VALID_ALPHA, Math.nextUp(1.0), VALID_GAMMA, VALID_DELTA);
122     }
123 
124     @Test
125     void testBetaNaNThrows() {
126         assertConstructorThrows(VALID_ALPHA, Double.NaN, VALID_GAMMA, VALID_DELTA);
127     }
128 
129     @Test
130     void testGammaNotStrictlyPositiveThrows() {
131         assertConstructorThrows(VALID_ALPHA, VALID_BETA, 0.0, VALID_DELTA);
132     }
133 
134     @Test
135     void testGammaInfThrows() {
136         assertConstructorThrows(VALID_ALPHA, VALID_BETA, Double.POSITIVE_INFINITY, VALID_DELTA);
137     }
138 
139     @Test
140     void testGammaNaNThrows() {
141         assertConstructorThrows(VALID_ALPHA, VALID_BETA, Double.NaN, VALID_DELTA);
142     }
143 
144     @Test
145     void testDeltaInfThrows() {
146         assertConstructorThrows(VALID_ALPHA, VALID_BETA, VALID_GAMMA, Double.POSITIVE_INFINITY);
147     }
148 
149     @Test
150     void testDeltaNegInfThrows() {
151         assertConstructorThrows(VALID_ALPHA, VALID_BETA, VALID_GAMMA, Double.NEGATIVE_INFINITY);
152     }
153 
154     @Test
155     void testDeltaNaNThrows() {
156         assertConstructorThrows(VALID_ALPHA, VALID_BETA, VALID_GAMMA, Double.NaN);
157     }
158 
159     /**
160      * Asserts the stable sampler factory constructor throws an {@link IllegalArgumentException}.
161      *
162      * @param alpha Stability parameter. Must be in range {@code (0, 2]}.
163      * @param beta Skewness parameter. Must be in range {@code [-1, 1]}.
164      * @param gamma Scale parameter. Must be strictly positive and finite.
165      * @param delta Location parameter. Must be finite.
166      */
167     private static void assertConstructorThrows(double alpha, double beta, double gamma, double delta) {
168         final UniformRandomProvider rng = new SplitMix64(0L);
169         Assertions.assertThrows(IllegalArgumentException.class,
170             () -> StableSampler.of(rng, alpha, beta, gamma, delta));
171     }
172 
173     /**
174      * Assumption test:
175      * Test the limits of the value {@code tau} at the extreme limits of {@code alpha}.
176      * The expression is evaluated against the original CMS algorithm. The method
177      * has been updated to ensure symmetry around zero.
178      *
179      * <p>The test demonstrates that tau can be zero even when beta is not zero. Thus
180      * the choice of a beta=0 sampler must check tau and not beta.
181      */
182     @Test
183     void testTauLimits() {
184         // At the limit of beta, tau ranges from 2/pi to 0 as alpha moves away from 1.
185         final double beta = 1;
186 
187         // alpha -> 2: tau -> 0
188         // alpha -> 0: tau -> 0
189         Assertions.assertEquals(0.0, CMSStableSampler.getTau(2, beta));
190         Assertions.assertEquals(0.0, CMSStableSampler.getTau(0, beta));
191 
192         // Full range over 0 to 2.
193         for (int i = 0; i <= 512; i++) {
194             // This is a power of 2 so the symmetric test uses an exact mirror
195             final double alpha = (double) i / 256;
196             final double tau = CMSStableSampler.getTau(alpha, beta);
197             final double expected = getTauOriginal(alpha, beta);
198             Assertions.assertEquals(expected, tau, 1e-15);
199 
200             // Symmetric
201             Assertions.assertEquals(tau, CMSStableSampler.getTau(2 - alpha, beta));
202         }
203 
204         // alpha -> 1: tau -> beta / (pi / 2) = 0.6366
205         final double limit = beta / PI_2;
206         Assertions.assertEquals(limit, CMSStableSampler.getTau(1, beta));
207         for (double alpha : new double[] {1.01, 1 + 1e-6, 1, 1 - 1e-6, 0.99}) {
208             final double tau = CMSStableSampler.getTau(alpha, beta);
209             final double expected = getTauOriginal(alpha, beta);
210             Assertions.assertEquals(expected, tau, 1e-15);
211             // Approach the limit
212             Assertions.assertEquals(limit, tau, Math.abs(1 - alpha) + 1e-15);
213         }
214 
215         // It can be zero if beta is zero or close to zero when alpha != 1.
216         // This requires we check tau==0 instead of beta==0 to switch to
217         // a beta = 0 sampler.
218         Assertions.assertEquals(0.0, CMSStableSampler.getTau(1.3, 0.0));
219         Assertions.assertEquals(0.0, CMSStableSampler.getTau(1.5, Double.MIN_VALUE));
220         Assertions.assertNotEquals(0.0, CMSStableSampler.getTau(1.0, Double.MIN_VALUE));
221 
222         // The sign of beta determines the sign of tau.
223         Assertions.assertEquals(0.5, CMSStableSampler.getTau(1.5, beta));
224         Assertions.assertEquals(0.5, CMSStableSampler.getTau(0.5, beta));
225         Assertions.assertEquals(-0.5, CMSStableSampler.getTau(1.5, -beta));
226         Assertions.assertEquals(-0.5, CMSStableSampler.getTau(0.5, -beta));
227 
228         // Check monototic at the transition point to switch to a different computation.
229         final double tau1 = CMSStableSampler.getTau(Math.nextDown(1.5), 1);
230         final double tau2 = CMSStableSampler.getTau(1.5, 1);
231         final double tau3 = CMSStableSampler.getTau(Math.nextUp(1.5), 1);
232         Assertions.assertTrue(tau1 > tau2);
233         Assertions.assertTrue(tau2 > tau3);
234         // Test symmetry at the transition
235         Assertions.assertEquals(tau1, CMSStableSampler.getTau(2 - Math.nextDown(1.5), 1));
236         Assertions.assertEquals(tau2, CMSStableSampler.getTau(0.5, 1));
237         Assertions.assertEquals(tau3, CMSStableSampler.getTau(2 - Math.nextUp(1.5), 1));
238     }
239 
240     /**
241      * Gets tau using the original method from the CMS algorithm implemented in the
242      * program RSTAB. This does not use {@link SpecialMath#tan2(double)} but uses
243      * {@link Math#tan(double)} to implement {@code tan(x) / x}.
244      *
245      * @param alpha alpha
246      * @param beta the beta
247      * @return tau
248      */
249     private static double getTauOriginal(double alpha, double beta) {
250         final double eps = 1 - alpha;
251         // Compute RSTAB prefactor
252         double tau;
253 
254         // Use the method from Chambers et al (1976).
255         // TAN2(x) = tan(x) / x
256         // PIBY2 = pi / 2
257         // Comments are the FORTRAN code from the RSTAB routine.
258 
259         if (eps > -0.99) {
260             // TAU = BPRIME / (TAN2(EPS * PIBY2) * PIBY2)
261             final double tan2 = eps == 0 ? 1 : Math.tan(eps * PI_2) / (eps * PI_2);
262             tau = beta / (tan2 * PI_2);
263         } else {
264             // TAU = BPRIME * PIBY2 * EPS * (1.-EPS) * TAN2 ((1. -EPS) * PIBY2)
265             final double meps1 = 1 - eps;
266             final double tan2 = Math.tan(meps1 * PI_2) / (meps1 * PI_2);
267             tau = beta * PI_2 * eps * meps1 * tan2;
268         }
269 
270         return tau;
271     }
272 
273     /**
274      * Assumption test:
275      * Test the value {@code a2} is not zero. Knowing {@code a2} is not zero simplifies
276      * correction of non-finite results from the CMS algorithm.
277      */
278     @Test
279     void testA2IsNotZero() {
280         // The extreme limit of the angle phiby2. This is ignored by the sampler
281         // as it can result in cancellation of terms and invalid results.
282         final double p0 = getU(Long.MIN_VALUE);
283         Assertions.assertEquals(-PI_4, p0);
284 
285         // These are the limits to generate (-pi/4, pi/4)
286         final double p1 = getU(Long.MIN_VALUE + (1 << 10));
287         final double p2 = getU(Long.MAX_VALUE);
288         Assertions.assertNotEquals(-PI_4, p1);
289         Assertions.assertNotEquals(PI_4, p2);
290         Assertions.assertEquals(-PI_4 + PI_4 * DU, p1);
291         Assertions.assertEquals(PI_4 - PI_4 * DU, p2);
292 
293         for (double phiby2 : new double[] {p1, p2}) {
294             // phiby2 in (-pi/4, pi/4)
295             // a in (-1, 1)
296             final double a = phiby2 * SpecialMath.tan2(phiby2);
297             Assertions.assertEquals(Math.copySign(Math.nextDown(1.0), phiby2), a);
298             final double da = a * a;
299             final double a2 = 1 - da;
300             // The number is close to but not equal to zero
301             Assertions.assertNotEquals(0.0, a2);
302             // The minimum value of a2 is 2.220E-16 = 2^-52
303             Assertions.assertEquals(0x1.0p-52, a2);
304         }
305     }
306 
307     /**
308      * Assumption test:
309      * Test the value of the numerator used to compute z. If this is negative then
310      * computation of log(z) creates a NaN. This effect occurs when the uniform
311      * random deviate u is either 0 or 1 and beta is -1 or 1. The effect is reduced
312      * when u is in the range {@code (0, 1)} but not eliminated. The test
313      * demonstrates: (a) the requirement to check z during the sample method when
314      * {@code alpha!=1}; and (b) when {@code alpha=1} then z cannot be zero when u
315      * is in the open interval {@code (0, 1)}.
316      */
317     @Test
318     void testZIsNotAlwaysAboveZero() {
319         // A long is used to create phi/2:
320         // The next to limit values for the phi/2
321         final long x00 = Long.MIN_VALUE;
322         final long x0 = Long.MIN_VALUE + (1 << 10);
323         final long x1 = Long.MAX_VALUE;
324         Assertions.assertEquals(-PI_4, getU(x00));
325         Assertions.assertEquals(-PI_4 + DU * PI_4, getU(x0));
326         Assertions.assertEquals(PI_4 - DU * PI_4, getU(x1));
327         // General case numerator:
328         // b2 + 2 * phiby2 * bb * tau
329         // To generate 0 numerator requires:
330         // b2 == -2 * phiby2 * bb * tau
331         //
332         // The expansion of the terms is:
333         // 1 - tan^2(eps * phi/2)
334         // == -phi * tan2(eps * phi/2) *        beta
335         //                               -----------------------
336         //                               tan2(eps * pi/2) * pi/2
337         //
338         // == -2 * phi * tan2(eps * phi/2) * beta
339         //    -----------------------------------
340         //          pi * tan2(eps * pi/2)
341         //
342         // == -2 * phi * tan(eps * phi/2) / (eps * phi/2) * beta
343         //    --------------------------------------------------
344         //          pi * tan(eps * pi/2) / (eps * pi/2)
345         //
346         // if phi/2 = pi/4, x = eps * phi/2:
347         // == -2 * pi/4 * tan(x) / (x) * beta
348         //    ---------------------------------
349         //          pi * tan(2x) / (2x)
350         //
351         // This is a known double-angle identity for tan:
352         // 1 - tan^2(x) == -2 tan(x) * beta
353         //                 ---------
354         //                  tan(2x)
355         // Thus if |beta|=1 with opposite sign to phi, and |phi|=pi/2
356         // the numerator is zero for all alpha.
357         // This is not true due to floating-point
358         // error but the following cases are known to exhibit the result.
359 
360         Assertions.assertEquals(0.0, computeNumerator(0.859375, 1, x00));
361         // Even worse to have negative as the log(-ve) = nan
362         Assertions.assertTrue(0.0 > computeNumerator(0.9375, 1, x00));
363         Assertions.assertTrue(0.0 > computeNumerator(1.90625, 1, x00));
364 
365         // As phi reduces in magnitude the equality fails.
366         // The numerator=0 can often be corrected
367         // with the next random variate from the range limit.
368         Assertions.assertTrue(0.0 < computeNumerator(0.859375, 1, x0));
369         Assertions.assertTrue(0.0 < computeNumerator(0.9375, 1, x0));
370         Assertions.assertTrue(0.0 < computeNumerator(1.90625, 1, x0));
371 
372         // WARNING:
373         // Even when u is not at the limit floating point error can still create
374         // a bad numerator. This is rare but shows we must still detect this edge
375         // case.
376         Assertions.assertTrue(0.0 > computeNumerator(0.828125, 1, x0));
377         Assertions.assertTrue(0.0 > computeNumerator(1.291015625, -1, x1));
378 
379         // beta=0 case the numerator reduces to b2:
380         // b2 = 1 - tan^2((1-alpha) * phi/2)
381         // requires tan(x)=1; x=pi/4.
382         // Note: tan(x) = x * SpecialMath.tan2(x) returns +/-1 for u = +/-pi/4.
383         Assertions.assertEquals(-1, SpecialMath.tan2(getU(x00)) * getU(x00));
384         // Using the next value in the range this is not an issue.
385         // The beta=0 sampler does not have to check for z=0.
386         Assertions.assertTrue(-1 < SpecialMath.tan2(getU(x0)) * getU(x0));
387         Assertions.assertTrue(1 > SpecialMath.tan2(getU(x1)) * getU(x1));
388         // Use alpha=2 so 1-alpha (eps) is at the limit
389         final double beta = 0;
390         Assertions.assertEquals(0.0, computeNumerator(2, beta, x00));
391         Assertions.assertTrue(0.0 < computeNumerator(2, beta, x0));
392         Assertions.assertTrue(0.0 < computeNumerator(2, beta, x1));
393         Assertions.assertTrue(0.0 < computeNumerator(Math.nextDown(2), beta, x0));
394         Assertions.assertTrue(0.0 < computeNumerator(Math.nextDown(2), beta, x1));
395 
396         // alpha=1 case the numerator reduces to:
397         // 1 + 2 * phi/2 * tau
398         // A zero numerator requires:
399         // 2 * phiby2 * tau = -1
400         //
401         // tau = 2 * beta / pi
402         // phiby2 = -pi / (2 * 2 * beta)
403         // beta = 1 => phiby2 = -pi/4
404         // beta = -1 => phiby2 = pi/4
405         // The alpha=1 sampler does not have to check for z=0 if phiby2 excludes -pi/4.
406         final double alpha = 1;
407         Assertions.assertEquals(0.0, computeNumerator(alpha, 1, x00));
408         // Next value of u computes above zero
409         Assertions.assertTrue(0.0 < computeNumerator(alpha, 1, x0));
410         Assertions.assertTrue(0.0 < computeNumerator(alpha, -1, x1));
411         // beta < 1 => u < 0
412         // beta > -1 => u > 1
413         // z=0 not possible with any other beta
414         Assertions.assertTrue(0.0 < computeNumerator(alpha, Math.nextUp(-1), x00));
415     }
416 
417     /**
418      * Compute the numerator value for the z coefficient in the CMS algorithm.
419      *
420      * @param alpha Stability parameter. Must be in range {@code (0, 2]}.
421      * @param beta Skewness parameter. Must be in range {@code [-1, 1]}.
422      * @param x The random long used to generate the uniform deviate in the range {@code [-pi/4, pi/4)}.
423      * @return numerator
424      */
425     private static double computeNumerator(double alpha, double beta, long x) {
426         final double phiby2 = getU(x);
427         final double eps = 1 - alpha;
428         final double tau = CMSStableSampler.getTau(alpha, beta);
429 
430         final double bb = SpecialMath.tan2(eps * phiby2);
431         final double b = eps * phiby2 * bb;
432         // Compute some necessary subexpressions
433         final double db = b * b;
434         final double b2 = 1 - db;
435         // Compute z coefficient numerator.
436         return b2 + 2 * phiby2 * bb * tau;
437     }
438 
439     /**
440      * Assumption test:
441      * Test the CMS algorithm can compute the value {@code d} without creating a NaN
442      * when {@code z} is any non-zero finite value. When the value {@code z} is zero or infinite
443      * the computation may multiply infinity by zero and create NaN.
444      */
445     @Test
446     void testComputeDWhenZIsFiniteNonZero() {
447         final double[] zs = {Double.MIN_VALUE, Double.MAX_VALUE};
448 
449         final double[] alphas = {2, 1.5, 1 + 1e-6, 1, 1 - 1e-6, 0.5, 0.01, 1e-10, SMALLEST_ALPHA};
450         for (final double alpha : alphas) {
451             // Finite z
452             for (final double z : zs) {
453                 // The result may be infinite, but not NaN
454                 Assertions.assertNotEquals(Double.NaN, computeD(alpha, z));
455             }
456 
457             // May be invalid with z=0 or z=inf as some combinations multiply the
458             // infinity by 0 to create NaN.
459 
460             // When z=0, log(z) = -inf, d = d2(sign(1-alpha) * -inf) * -inf
461             final double d0 = computeD(alpha, 0);
462             if (alpha < 1) {
463                 // d2(-inf) * -inf = 0 * -inf = NaN
464                 Assertions.assertEquals(Double.NaN, d0);
465             } else if (alpha == 1) {
466                 // d2(0 * -inf) -> NaN
467                 Assertions.assertEquals(Double.NaN, d0);
468             } else {
469                 // alpha > 1
470                 // d2(inf) * -inf = -inf
471                 Assertions.assertEquals(Double.NEGATIVE_INFINITY, d0);
472             }
473 
474             // When z=inf, log(z) = inf, d = d2(sign(1-alpha) * inf) * inf
475             final double di = computeD(alpha, Double.POSITIVE_INFINITY);
476             if (alpha < 1) {
477                 // d2(inf) * inf = inf
478                 Assertions.assertEquals(Double.POSITIVE_INFINITY, di);
479             } else if (alpha == 1) {
480                 // d2(0 * inf) -> NaN
481                 Assertions.assertEquals(Double.NaN, di);
482             } else {
483                 // alpha > 1
484                 // d2(-inf) * inf = 0 * inf = NaN
485                 Assertions.assertEquals(Double.NaN, di);
486             }
487         }
488     }
489 
490     /**
491      * Compute the {@code d} value in the CMS algorithm.
492      *
493      * @param alpha alpha
494      * @param z z
495      * @return d
496      */
497     private static double computeD(double alpha, double z) {
498         final double alogz = Math.log(z);
499         final double eps = 1 - alpha;
500         final double meps1 = 1 - eps;
501         return SpecialMath.d2(eps * alogz / meps1) * (alogz / meps1);
502     }
503 
504     /**
505      * Assumption test:
506      * Test the sin(alpha * phi + atan(-zeta)) term can be zero.
507      * This applies to the Weron formula.
508      */
509     @Test
510     void testSinAlphaPhiMinusAtanZeta() {
511         // Note sin(alpha * phi + atan(-zeta)) is zero when:
512         // alpha * phi = -atan(-zeta)
513         // tan(-alpha * phi) = -zeta
514         //                   = beta * tan(alpha * pi / 2)
515         // beta = tan(-alpha * phi) / tan(alpha * pi / 2)
516         // Find a case where the result is zero...
517         for (double alpha : new double[] {0.25, 0.125}) {
518             for (double phi : new double[] {PI_4, PI_4 / 2}) {
519                 double beta = Math.tan(-alpha * phi) / Math.tan(alpha * PI_2);
520                 double zeta = -beta * Math.tan(alpha * PI_2);
521                 double atanZeta = Math.atan(-zeta);
522                 Assertions.assertEquals(0.0, alpha * phi + atanZeta);
523             }
524         }
525     }
526 
527     /**
528      * Assumption test:
529      * Test the cos(phi - alpha * (phi + xi)) term is positive.
530      * This applies to the Weron formula.
531      */
532     @Test
533     void testCosPhiMinusAlphaPhiXi() {
534         // This is the extreme of cos(x) that should be used
535         final double cosPi2 = Math.cos(PI_2);
536         // The function is symmetric
537         Assertions.assertEquals(cosPi2, Math.cos(-PI_2));
538         // As pi is an approximation then the cos value is not exactly 0
539         Assertions.assertTrue(cosPi2 > 0);
540 
541         final UniformRandomProvider rng = RandomAssert.createRNG();
542 
543         // The term is mirrored around 1 so use extremes between 1 and 0
544         final double[] alphas = {1, Math.nextDown(1), 0.99, 0.5, 0.1, 0.05, 0.01, DU};
545         // Longs to generate extremes for the angle phi. This is mirrored
546         // by negation is the assert method so use values to create phi in [0, pi/2).
547         final long[] xs = {0, 1 << 10, Long.MIN_VALUE >>> 1, Long.MAX_VALUE};
548         for (final double alpha : alphas) {
549             for (final long x : xs) {
550                 assertCosPhiMinusAlphaPhiXi(alpha, x);
551                 assertCosPhiMinusAlphaPhiXi(2 - alpha, x);
552             }
553             for (int j = 0; j < 1000; j++) {
554                 final long x = rng.nextLong();
555                 assertCosPhiMinusAlphaPhiXi(alpha, x);
556                 assertCosPhiMinusAlphaPhiXi(2 - alpha, x);
557             }
558         }
559         // Random alpha
560         for (int i = 0; i < 1000; i++) {
561             final double alpha = rng.nextDouble();
562             for (final long x : xs) {
563                 assertCosPhiMinusAlphaPhiXi(alpha, x);
564                 assertCosPhiMinusAlphaPhiXi(2 - alpha, x);
565             }
566             for (int j = 0; j < 1000; j++) {
567                 final long x = rng.nextLong();
568                 assertCosPhiMinusAlphaPhiXi(alpha, x);
569                 assertCosPhiMinusAlphaPhiXi(2 - alpha, x);
570             }
571         }
572 
573         // Enumerate alpha
574         for (int i = 0; i <= 1023; i++)  {
575             final double alpha = (double) i / 1023;
576             for (final long x : xs) {
577                 assertCosPhiMinusAlphaPhiXi(alpha, x);
578                 assertCosPhiMinusAlphaPhiXi(2 - alpha, x);
579             }
580         }
581     }
582 
583     /**
584      * Assert the cos(phi - alpha * (phi + xi)) term is positive.
585      * This asserts the term (phi - alpha * (phi + xi)) is in the interval (-pi/2, pi/2) when
586      * beta is the extreme of +/-1.
587      *
588      * @param alpha alpha
589      * @param x the long used to create the uniform deviate
590      */
591     private static void assertCosPhiMinusAlphaPhiXi(double alpha, long x) {
592         // Update for symmetry around alpha = 1
593         final double eps = 1 - alpha;
594         final double meps1 = 1 - eps;
595 
596         // zeta = -beta * tan(alpha * pi / 2)
597         // xi = atan(-zeta) / alpha
598         // Compute phi - alpha * (phi + xi).
599         // This value must be in (-pi/2, pi/2).
600         // The term expands to:
601         // phi - alpha * (phi + xi)
602         // = phi - alpha * phi - atan(-zeta)
603         // = (1-alpha) * phi - atan(-zeta)
604         // When beta = +/-1,
605         // atanZeta = +/-alpha * pi/2  if alpha < 1
606         // atanZeta = +/-(2-alpha) * pi/2  if alpha > 1
607         // alpha=1 => always +/-pi/2
608         // alpha=0,2 => always +/-phi
609         // Values in between use the addition:
610         // (1-alpha) * phi +/- alpha * pi/2
611         // Since (1-alpha) is exact and alpha = 1 - (1-alpha) the addition
612         // cannot exceed pi/2.
613 
614         // Avoid the round trip using tan and arctan when beta is +/- 1
615         // zeta = -beta * Math.tan(alpha * pi / 2);
616         // atan(-zeta) = alpha * pi / 2
617 
618         final double alphaPi2;
619         if (meps1 > 1) {
620             // Avoid calling tan outside the domain limit [-pi/2, pi/2].
621             alphaPi2 = -(2 - meps1) * PI_2;
622         } else {
623             alphaPi2 = meps1 * PI_2;
624         }
625 
626         // Compute eps * phi +/- alpha * pi / 2
627         // Test it is in the interval (-pi/2, pi/2)
628         double phi = getU(x) * 2;
629         double value = eps * phi + alphaPi2;
630         Assertions.assertTrue(value <= PI_2);
631         Assertions.assertTrue(value >= -PI_2);
632         value = eps * phi - alphaPi2;
633         Assertions.assertTrue(value <= PI_2);
634         Assertions.assertTrue(value >= -PI_2);
635 
636         // Mirror the deviate
637         phi = -phi;
638         value = eps * phi + alphaPi2;
639         Assertions.assertTrue(value <= PI_2);
640         Assertions.assertTrue(value >= -PI_2);
641         value = eps * phi - alphaPi2;
642         Assertions.assertTrue(value <= PI_2);
643         Assertions.assertTrue(value >= -PI_2);
644     }
645     /**
646      * Assumption test:
647      * Test the sin(alpha * phi) term is only zero when phi is zero.
648      * This applies to the Weron formula when {@code beta = 0}.
649      */
650     @Test
651     void testSinAlphaPhi() {
652         // Smallest non-zero phi.
653         // getU creates in the domain (-pi/4, pi/4) so double the angle.
654         for (final double phi : new double[] {getU(-1) * 2, getU(1 << 10) * 2}) {
655             final double x = Math.sin(SMALLEST_ALPHA * phi);
656             Assertions.assertNotEquals(0.0, x);
657             // Value is actually:
658             Assertions.assertEquals(1.9361559566769725E-32, Math.abs(x));
659         }
660     }
661 
662     /**
663      * Assumption test:
664      * Test functions to compute {@code (exp(x) - 1) / x}. This tests the use of
665      * {@link Math#expm1(double)} and {@link Math#exp(double)} to determine if the switch
666      * point to the high precision version is monotonic.
667      */
668     @Test
669     void testExpM1() {
670         // Test monotonic at the switch point
671         Assertions.assertEquals(d2(0.5), d2b(0.5));
672         // When positive x -> 0 the value smaller bigger.
673         Assertions.assertTrue(d2(Math.nextDown(0.5)) <= d2b(0.5));
674         Assertions.assertEquals(d2(-0.5), d2b(-0.5));
675         // When negative x -> 0 the value gets bigger.
676         Assertions.assertTrue(d2(-Math.nextDown(0.5)) >= d2b(-0.5));
677         // Potentially the next power of 2 could be used based on ULP errors but
678         // the switch is not monotonic.
679         Assertions.assertFalse(d2(Math.nextDown(0.25)) <= d2b(0.25));
680     }
681 
682     /**
683      * This is not a test.
684      *
685      * <p>This outputs a report of the mean ULP difference between
686      * using {@link Math#expm1(double)} and {@link Math#exp(double)} to evaluate
687      * {@code (exp(x) - 1) / x}. This helps choose the switch point to avoid the computationally
688      * expensive expm1 function.
689      */
690     //@Test
691     void expm1ULPReport() {
692         // Create random doubles with a given exponent. Compute the mean and max ULP difference.
693         final UniformRandomProvider rng = RandomAssert.createRNG();
694         // For a quicker report set to <= 2^20.
695         final int size = 1 << 30;
696         // Create random doubles using random bits in the 52-bit mantissa.
697         final long mask = (1L << 52) - 1;
698         // Note:
699         // The point at which there *should* be no difference between the two is when
700         // exp(x) - 1 == exp(x). This will occur at exp(x)=2^54, x = ln(2^54) = 37.43.
701         Assertions.assertEquals(((double) (1L << 54)) - 1, (double) (1L << 54));
702         // However since expm1 and exp are only within 1 ULP of the exact result differences
703         // still occur above this threshold.
704         // 2^6 = 64; 2^-4 = 0.0625
705         for (int signedExp = 6; signedExp >= -4; signedExp--) {
706             // The exponent must be unsigned so + 1023 to the signed exponent
707             final long exp = (signedExp + 1023L) << 52;
708             // Test we are creating the correct numbers
709             Assertions.assertEquals(signedExp, Math.getExponent(Double.longBitsToDouble(exp)));
710             Assertions.assertEquals(signedExp, Math.getExponent(Double.longBitsToDouble((-1 & mask) | exp)));
711             // Get the average and max ulp
712             long sum1 = 0;
713             long sum2 = 0;
714             long max1 = 0;
715             long max2 = 0;
716             for (int i = size; i-- > 0;) {
717                 final long bits = rng.nextLong() & mask;
718                 final double x = Double.longBitsToDouble(bits | exp);
719                 final double x1 = d2(x);
720                 final double x2 = d2b(x);
721                 final double x1b = d2(-x);
722                 final double x2b = d2b(-x);
723                 final long ulp1 = Math.abs(Double.doubleToRawLongBits(x1) - Double.doubleToRawLongBits(x2));
724                 final long ulp2 = Math.abs(Double.doubleToRawLongBits(x1b) - Double.doubleToRawLongBits(x2b));
725                 sum1 += ulp1;
726                 sum2 += ulp2;
727                 if (max1 < ulp1) {
728                     max1 = ulp1;
729                 }
730                 if (max2 < ulp2) {
731                     max2 = ulp2;
732                 }
733             }
734             // CHECKSTYLE: stop Regexp
735             System.out.printf("%-6s   %2d   %-24s (%d)   %-24s (%d)%n",
736                 Double.longBitsToDouble(exp), signedExp,
737                 (double) sum1 / size, max1, (double) sum2 / size, max2);
738             // CHECKSTYLE: resume Regexp
739         }
740     }
741 
742     /**
743      * Evaluate {@code (exp(x) - 1) / x} using {@link Math#expm1(double)}.
744      * For {@code x} in the range {@code [-inf, inf]} returns
745      * a result in {@code [0, inf]}.
746      *
747      * <ul>
748      * <li>For {@code x=-inf} this returns {@code 0}.
749      * <li>For {@code x=0} this returns {@code 1}.
750      * <li>For {@code x=inf} this returns {@code inf}.
751      * </ul>
752      *
753      * <p> This corrects {@code 0 / 0} and {@code inf / inf} division from
754      * {@code NaN} to either {@code 1} or the upper bound respectively.
755      *
756      * @param x value to evaluate
757      * @return {@code (exp(x) - 1) / x}.
758      */
759     private static double d2(double x) {
760         // Here we use a conditional to detect both edge cases, which are then corrected.
761         final double d2 = Math.expm1(x) / x;
762         if (Double.isNaN(d2)) {
763             // Correct edge cases.
764             if (x == 0) {
765                 return 1.0;
766             }
767             // x must have been +infinite or NaN
768             return x;
769         }
770         return d2;
771     }
772 
773     /**
774      * Evaluate {@code (exp(x) - 1) / x} using {@link Math#exp(double)}.
775      * For {@code x} in the range {@code [-inf, inf]} returns
776      * a result in {@code [0, inf]}.
777      *
778      * <ul>
779      * <li>For {@code x=-inf} this returns {@code 0}.
780      * <li>For {@code x=0} this returns {@code 1}.
781      * <li>For {@code x=inf} this returns {@code inf}.
782      * </ul>
783      *
784      * <p> This corrects {@code 0 / 0} and {@code inf / inf} division from
785      * {@code NaN} to either {@code 1} or the upper bound respectively.
786      *
787      * @param x value to evaluate
788      * @return {@code (exp(x) - 1) / x}.
789      */
790     private static double d2b(double x) {
791         // Here we use a conditional to detect both edge cases, which are then corrected.
792         final double d2 = (Math.exp(x) - 1) / x;
793         if (Double.isNaN(d2)) {
794             // Correct edge cases.
795             if (x == 0) {
796                 return 1.0;
797             }
798             // x must have been +infinite or NaN
799             return x;
800         }
801         return d2;
802     }
803 
804     /**
805      * Test the special d2 function returns {@code (exp(x) - 1) / x}.
806      * The limits of the function are {@code [0, inf]} and it should return 1 when x=0.
807      */
808     @Test
809     void testD2() {
810         for (final double x : new double[] {Double.MAX_VALUE, Math.log(Double.MAX_VALUE), 10, 5, 1, 0.5, 0.1, 0.05, 0.01}) {
811             Assertions.assertEquals(Math.expm1(x) / x, SpecialMath.d2(x), 1e-15);
812             Assertions.assertEquals(Math.expm1(-x) / -x, SpecialMath.d2(-x), 1e-15);
813         }
814 
815         // Negative infinity computes without correction
816         Assertions.assertEquals(0.0, Math.expm1(Double.NEGATIVE_INFINITY) / Double.NEGATIVE_INFINITY);
817         Assertions.assertEquals(0.0, SpecialMath.d2(Double.NEGATIVE_INFINITY));
818 
819         // NaN is returned (i.e. no correction)
820         Assertions.assertEquals(Double.NaN, SpecialMath.d2(Double.NaN));
821 
822         // Edge cases for z=0 or z==inf require correction
823         Assertions.assertEquals(Double.NaN, Math.expm1(0) / 0.0);
824         Assertions.assertEquals(Double.NaN, Math.expm1(Double.POSITIVE_INFINITY) / Double.POSITIVE_INFINITY);
825         // Corrected in the special function
826         Assertions.assertEquals(1.0, SpecialMath.d2(0.0));
827         Assertions.assertEquals(Double.POSITIVE_INFINITY, SpecialMath.d2(Double.POSITIVE_INFINITY));
828     }
829 
830     /**
831      * Test the tan2 function returns {@code tan(x) / x}.
832      */
833     @Test
834     void testTan2() {
835         // Test the value of tan(x) when the angle is generated in the open interval (-pi/4, pi/4)
836         for (final long x : new long[] {Long.MIN_VALUE + (1 << 10), Long.MAX_VALUE}) {
837             final double phiby2 = getU(x);
838             Assertions.assertEquals(PI_4 - DU * PI_4, Math.abs(phiby2));
839             final double a = phiby2 * SpecialMath.tan2(phiby2);
840             // Check this is not 1
841             Assertions.assertNotEquals(1, Math.abs(a));
842             Assertions.assertTrue(Math.abs(a) < 1.0);
843         }
844 
845         // At pi/4 the function reverts to Math.tan(x) / x. Test through the transition.
846         final double pi = Math.PI;
847         for (final double x : new double[] {pi, pi / 2, pi / 3.99, pi / 4, pi / 4.01, pi / 8, pi / 16}) {
848             final double y = Math.tan(x) / x;
849             Assertions.assertEquals(y, SpecialMath.tan2(x), Math.ulp(y));
850         }
851 
852         // Test this closely matches the JDK tan function.
853         // Test uniformly between 0 and pi / 4.
854         // Count the errors with the ULP difference.
855         // Get max ULP and mean ULP. Do this for both tan(x) and tan(x)/x functions.
856         final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create(0x1647816481684L);
857         int count = 0;
858         long ulp = 0;
859         long max = 0;
860         long ulp2 = 0;
861         long max2 = 0;
862         for (int i = 0; i < 1000; i++) {
863             final double x = rng.nextDouble() * PI_4;
864             count++;
865             final double tanx = Math.tan(x);
866             final double tan2x = SpecialMath.tan2(x);
867             // Test tan(x)
868             double y = x * tan2x;
869             if (y != tanx) {
870                 final long u = Math.abs(Double.doubleToRawLongBits(tanx) - Double.doubleToRawLongBits(y));
871                 if (max < u) {
872                     max = u;
873                 }
874                 ulp += u;
875                 // Within 4 ulp. Note tan(x) is within 1 ulp of the result. So this
876                 // is max 5 ulp from the result.
877                 Assertions.assertEquals(tanx, y, 4 * Math.ulp(tanx));
878             }
879             // Test tan(x) / x
880             y = tanx / x;
881             if (y != tan2x) {
882                 final long u = Math.abs(Double.doubleToRawLongBits(tan2x) - Double.doubleToRawLongBits(y));
883                 if (max2 < u) {
884                     max2 = u;
885                 }
886                 ulp2 += u;
887                 // Within 3 ulp.
888                 Assertions.assertEquals(y, tan2x, 3 * Math.ulp(y));
889             }
890         }
891         // Mean (max) ULP is very low
892         // 2^30 random samples in [0, pi / 4)
893         //          tan(x)                    tan(x) / x
894         // tan4283  93436.25534446817         201185 : 68313.16171793547         128079
895         // tan4288c     0.5905972588807344         4 :     0.4047176940366626         3
896         Assertions.assertTrue((double) ulp / count < 0.6, "Mean ULP to tan(x) is too high");
897         Assertions.assertTrue((double) ulp2 / count < 0.45, "Mean ULP to tan(x) / x is too high");
898         // If the value is under 1 then the sampler will break due to cancellation errors.
899         Assertions.assertEquals(1.0, SpecialMath.tan2(0.0), "Must be exact tan(x) / x at x=0");
900         Assertions.assertEquals(4 / Math.PI, SpecialMath.tan2(PI_4), Math.ulp(4 / Math.PI));
901         Assertions.assertEquals(1.0, PI_4 * SpecialMath.tan2(PI_4), Math.ulp(1.0));
902         // If this is above 1 then the sampler will break. Test at the switch point pi/4.
903         Assertions.assertTrue(1.0 >= PI_4 * SpecialMath.tan2(PI_4));
904         Assertions.assertTrue(1.0 >= PI_4 * SpecialMath.tan2(Math.nextDown(PI_4)));
905         // Monotonic function at the transition
906         Assertions.assertTrue(SpecialMath.tan2(Math.nextUp(PI_4)) >= SpecialMath.tan2(PI_4));
907     }
908 
909     /**
910      * Assumption test:
911      * Demonstrate the CMS algorithm matches the Weron formula when {@code alpha != 1}.
912      * This shows the two are equivalent; they should match as the formulas are rearrangements.
913      */
914     @Test
915     void testSamplesWithAlphaNot1() {
916         // Use non-extreme parameters. beta and u are negated so use non-redundant values
917         final double[] alphas = {0.3, 0.9, 1.1, 1.5};
918         final double[] betas = {-1, -0.5, -0.3, 0};
919         final double[] ws = {0.1, 1, 3};
920         final double[] us = {0.1, 0.25, 0.5, 0.8};
921 
922         final double relative = 1e-5;
923         final double absolute = 1e-10;
924         for (final double alpha : alphas) {
925             for (final double beta : betas) {
926                 for (final double w : ws) {
927                     for (final double u : us) {
928                         final double x = sampleCMS(alpha, beta, w, u);
929                         final double y = sampleWeronAlphaNot1(alpha, beta, w, u);
930                         Assertions.assertEquals(x, y, Math.max(absolute, Math.abs(x) * relative));
931                         // Test symmetry
932                         final double z = sampleCMS(alpha, -beta, w, 1 - u);
933                         Assertions.assertEquals(x, -z, 0.0);
934                     }
935                 }
936             }
937         }
938     }
939 
940     /**
941      * Assumption test:
942      * Demonstrate the CMS algorithm matches the Weron formula when {@code alpha == 1}.
943      * This shows the two are equivalent; they should match as the formulas are rearrangements.
944      */
945     @Test
946     void testSamplesWithAlpha1() {
947         // Use non-extreme parameters. beta and u are negated so use non-redundant values
948         final double[] betas = {-1, -0.5, -0.3, 0};
949         final double[] ws = {0.1, 1, 3};
950         final double[] us = {0.1, 0.25, 0.5, 0.8};
951 
952         final double relative = 1e-5;
953         final double absolute = 1e-10;
954         final double alpha = 1;
955         for (final double beta : betas) {
956             for (final double w : ws) {
957                 for (final double u : us) {
958                     final double x = sampleCMS(alpha, beta, w, u);
959                     final double y = sampleWeronAlpha1(beta, w, u);
960                     Assertions.assertEquals(x, y, Math.max(absolute, Math.abs(x) * relative));
961                     // Test symmetry
962                     final double z = sampleCMS(alpha, -beta, w, 1 - u);
963                     Assertions.assertEquals(x, -z, 0.0);
964                 }
965             }
966         }
967     }
968 
969     /**
970      * Assumption test:
971      * Demonstrate the CMS formula is continuous as {@code alpha -> 1}.
972      * Demonstrate the Weron formula is not continuous as {@code alpha -> 1}.
973      */
974     @Test
975     void testConvergenceWithAlphaCloseTo1() {
976         final double[] betas = {-1, -0.5, 0, 0.3, 1};
977         final double[] ws = {0.1, 1, 10};
978         final double[] us = {0.1, 0.25, 0.5, 0.8};
979         final int steps = 30;
980 
981         // Start with alpha not close to 0. The value 0.0625 is a power of 2 so is scaled
982         // exactly by dividing by 2. With 30 steps this ranges from 2^-4 to 2^-34 leaving alpha:
983         // 1.0625 -> 1.0000000000582077 or
984         // 0.9375 -> 0.9999999999417923.
985         for (double deltaStart : new double[] {-0.0625, 0.0625}) {
986             // As alpha approaches 1 the value should approach the value when alpha=0.
987             // Count the number of times it get further away with a change of alpha.
988             int cmsCount = 0;
989             int weronCount = 0;
990 
991             for (final double beta : betas) {
992                 for (final double w : ws) {
993                     for (final double u : us) {
994                         // CMS formulas
995                         double x0 = sampleCMS(1, beta, w, u);
996                         Assertions.assertTrue(Double.isFinite(x0), "Target must be finite");
997 
998                         // Sample should approach x0 as alpha approaches 1
999                         double delta = deltaStart;
1000                         double dx = Math.abs(x0 - sampleCMS(1 + delta, beta, w, u));
1001                         for (int i = 0; i < steps; i++) {
1002                             delta /= 2;
1003                             final double dx2 = Math.abs(x0 - sampleCMS(1 + delta, beta, w, u));
1004                             if (dx2 > dx) {
1005                                 cmsCount++;
1006                             }
1007                             dx = dx2;
1008                         }
1009 
1010                         // Weron formulas
1011                         x0 = sampleWeronAlpha1(beta, w, u);
1012                         Assertions.assertTrue(Double.isFinite(x0), "Target must be finite");
1013 
1014                         // Sample should approach x0 as alpha approaches 1
1015                         delta = deltaStart;
1016                         dx = Math.abs(x0 - sampleWeronAlphaNot1(1 + delta, beta, w, u));
1017                         for (int i = 0; i < steps; i++) {
1018                             delta /= 2;
1019                             final double dx2 = Math.abs(x0 - sampleWeronAlphaNot1(1 + delta, beta, w, u));
1020                             if (dx2 > dx) {
1021                                 weronCount++;
1022                             }
1023                             dx = dx2;
1024                         }
1025                     }
1026                 }
1027             }
1028 
1029             // The CMS formala monotonically converges
1030             Assertions.assertEquals(0, cmsCount);
1031             // The weron formula does not monotonically converge
1032             // (difference to the target can be bigger when alpha moves closer to 1).
1033             Assertions.assertTrue(weronCount > 200);
1034         }
1035     }
1036 
1037     /**
1038      * Test extreme inputs to the CMS algorithm where {@code alpha != 1} and/or
1039      * {@code beta != 0}. These demonstrate cases where the parameters and the
1040      * random variates will create non-finite samples. The test checks that the Weron
1041      * formula can create an appropriate sample for all cases where the CMS formula fails.
1042      */
1043     @Test
1044     void testExtremeInputsToSample() {
1045         // Demonstrate instability when w = 0
1046         Assertions.assertEquals(Double.NaN, sampleCMS(1.3, 0.7, 0, 0.25));
1047         Assertions.assertTrue(Double.isFinite(sampleCMS(1.3, 0.7, SMALL_W, 0.25)));
1048 
1049         // Demonstrate instability when u -> 0 or 1, and |beta| = 1
1050         Assertions.assertEquals(Double.NaN, sampleCMS(1.1, 1.0, 0.1, 0));
1051         Assertions.assertTrue(Double.isFinite(sampleCMS(1.1, 1.0, 0.1, DU)));
1052 
1053         // Demonstrate instability when alpha -> 0
1054 
1055         // Small alpha does not tolerate very small w.
1056         Assertions.assertEquals(Double.NaN, sampleCMS(0.01, 0.7, SMALL_W, 0.5));
1057 
1058         // Very small alpha does not tolerate u approaching 0 or 1 (depending on the
1059         // skew)
1060         Assertions.assertEquals(Double.NaN, sampleCMS(1e-5, 0.7, 1.0, 1e-4));
1061         Assertions.assertEquals(Double.NaN, sampleCMS(1e-5, -0.7, 1.0, 1 - 1e-4));
1062 
1063         final double[] alphas = {Math.nextDown(2), 1.3, 1.1, Math.nextUp(1), 1, Math.nextDown(1), 0.7, 0.1, 0.05, 0.01, 0x1.0p-16};
1064         final double[] betas = {1, 0.9, 0.001, 0};
1065         // Avoid zero for the exponential sample.
1066         // Test the smallest non-zero sample from the ArhensDieter exponential sampler,
1067         // and the largest sample.
1068         final double[] ws = {0, SMALL_W, 0.001, 1, 10, LARGE_W};
1069         // The algorithm requires a uniform deviate in (0, 1).
1070         // Use extremes of the 2^53 dyadic rationals in (0, 1) up to the symmetry limit
1071         // (i.e. 0.5).
1072         final double[] us = {DU, 2 * DU, 0.0001, 0.5 - DU, 0.5};
1073 
1074         int nan1 = 0;
1075 
1076         for (final double alpha : alphas) {
1077             for (final double beta : betas) {
1078                 if (alpha == 1 && beta == 0) {
1079                     // Ignore the Cauchy case
1080                     continue;
1081                 }
1082                 // Get the support of the distribution. This is not -> +/-infinity
1083                 // when alpha < 1 and beta = +/-1.
1084                 final double[] support = getSupport(alpha, beta);
1085                 final double lower = support[0];
1086                 final double upper = support[1];
1087                 for (final double w : ws) {
1088                     for (final double u : us) {
1089                         final double x1 = sampleCMS(alpha, beta, w, u);
1090                         final double x2 = sampleWeron(alpha, beta, w, u);
1091 
1092                         if (Double.isNaN(x1)) {
1093                             nan1++;
1094                         }
1095                         // The edge-case corrected Weron formula should not fail
1096                         Assertions.assertNotEquals(Double.NaN, x2);
1097 
1098                         // Check symmetry of each formula.
1099                         // Use a delta of zero to allow equality of 0.0 and -0.0.
1100                         Assertions.assertEquals(x1, 0.0 - sampleCMS(alpha, -beta, w, 1 - u), 0.0);
1101                         Assertions.assertEquals(x2, 0.0 - sampleWeron(alpha, -beta, w, 1 - u), 0.0);
1102 
1103                         if (Double.isInfinite(x1) && x1 != x2) {
1104                             // Check the Weron correction for extreme samples.
1105                             // The result should be at the correct *finite* support bounds.
1106                             // Note: This applies when alpha < 1 and beta = +/-1.
1107                             Assertions.assertTrue(lower <= x2 && x2 <= upper);
1108                         }
1109                     }
1110                 }
1111             }
1112         }
1113 
1114         // The CMS algorithm is expected to fail some cases
1115         Assertions.assertNotEquals(0, nan1);
1116     }
1117 
1118     /**
1119      * Create a sample from a stable distribution. This is an implementation of the CMS
1120      * algorithm to allow exploration of various input values. The algorithm matches that
1121      * in the {@link CMSStableSampler} with the exception that the uniform variate
1122      * is provided in {@code (0, 1)}, not{@code (-pi/4, pi/4)}.
1123      *
1124      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
1125      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
1126      * @param w Exponential variate
1127      * @param u Uniform variate
1128      * @return the sample
1129      */
1130     private static double sampleCMS(double alpha, double beta, double w, double u) {
1131         final double phiby2 = PI_2 * (u - 0.5);
1132         final double eps = 1 - alpha;
1133         // Do not use alpha in place of 1 - eps. When alpha < 0.5, 1 - eps == alpha is not
1134         // always true as the reverse is not exact.
1135         final double meps1 = 1 - eps;
1136 
1137         // Compute RSTAB prefactor
1138         final double tau = CMSStableSampler.getTau(alpha, beta);
1139 
1140         // Generic stable distribution that is continuous as alpha -> 1.
1141         // This is a trigonomic rearrangement of equation 4.1 from Chambers et al (1976)
1142         // as implemented in the Fortran program RSTAB.
1143         // Uses the special functions:
1144         // tan2 = tan(x) / x
1145         // d2 = (exp(x) - 1) / x
1146         // Here tan2 is implemented using an high precision approximation.
1147 
1148         // Compute some tangents
1149         // Limits for |phi/2| < pi/4
1150         // a in (-1, 1)
1151         final double a = phiby2 * SpecialMath.tan2(phiby2);
1152         // bb in [1, 4/pi)
1153         final double bb = SpecialMath.tan2(eps * phiby2);
1154         // b in (-1, 1)
1155         final double b = eps * phiby2 * bb;
1156         // Compute some necessary subexpressions
1157         final double da = a * a;
1158         final double db = b * b;
1159         // a2 in (0, 1]
1160         final double a2 = 1 - da;
1161         // a2p in [1, 2)
1162         final double a2p = 1 + da;
1163         // b2 in (0, 1]
1164         final double b2 = 1 - db;
1165         // b2p in [1, 2)
1166         final double b2p = 1 + db;
1167         // Compute coefficient.
1168         // Note:
1169         // Avoid z <= 0 to avoid log(z) as negative infinity or nan.
1170         // This occurs when |phiby2| -> +/-pi/4 and |beta| -> 1.
1171         // Problems:
1172         // numerator=0 => z=0
1173         // denominator=0 => z=inf
1174         // numerator=denominator=0 => z=nan
1175         // 1. w or a2 are zero so the denominator is zero. w can be a rare exponential sample.
1176         // a2 -> zero if the uniform deviate is 0 or 1 and angle is |pi/4|.
1177         // If w -> 0 and |u-0.5| -> 0.5 then the product of w * a2 can be zero.
1178         // 2. |eps|=1, phiby2=|pi/4| => bb=4/pi, b=1, b2=0; if tau=0 then the numerator is zero.
1179         // This requires beta=0.
1180 
1181         final double z = a2p * (b2 + 2 * phiby2 * bb * tau) / (w * a2 * b2p);
1182         // Compute the exponential-type expression
1183         final double alogz = Math.log(z);
1184         final double d = SpecialMath.d2(eps * alogz / meps1) * (alogz / meps1);
1185 
1186         // Compute stable
1187         return (1 + eps * d) *
1188                 (2 * ((a - b) * (1 + a * b) - phiby2 * tau * bb * (b * a2 - 2 * a))) /
1189                 (a2 * b2p) + tau * d;
1190     }
1191 
1192     /**
1193      * Create a sample from a stable distribution. This is an implementation of the Weron formula.
1194      * The formula has been modified when alpha != 1 to return the 0-parameterization result and
1195      * correct extreme samples to +/-infinity.
1196      *
1197      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
1198      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
1199      * @param w Exponential variate
1200      * @param u Uniform variate
1201      * @return the sample
1202      * @see <a href="https://doi.org/10.1016%2F0167-7152%2895%2900113-1">Weron, R (1996).
1203      * "On the Chambers-Mallows-Stuck method for simulating skewed stable random variables".
1204      * Statistics &amp; Probability Letters. 28 (2): 165–171.</a>
1205      */
1206     private static double sampleWeron(double alpha, double beta, double w, double u) {
1207         return alpha == 1 ? sampleWeronAlpha1(beta, w, u) : sampleWeronAlphaNot1(alpha, beta, w, u);
1208     }
1209 
1210     /**
1211      * Create a sample from a stable distribution. This is an implementation of the
1212      * Weron {@code alpha != 1} formula. The formula has been modified to return the
1213      * 0-parameterization result and correct extreme samples to +/-infinity. The
1214      * algorithm matches that in the {@link WeronStableSampler} with the exception that
1215      * the uniform variate is provided in {@code (0, 1)}, not{@code (-pi/2, pi/2)}.
1216      *
1217      * <p>Due to the increasingly large shift (up to 1e16) as {@code alpha -> 1}
1218      * that is used to move the result to the 0-parameterization the samples around
1219      * the mode of the distribution have large cancellation and a reduced number of
1220      * bits in the sample value.
1221      *
1222      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
1223      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
1224      * @param w Exponential variate
1225      * @param u Uniform variate
1226      * @return the sample
1227      * @see <a href="https://doi.org/10.1016%2F0167-7152%2895%2900113-1">Weron, R
1228      * (1996).
1229      * "On the Chambers-Mallows-Stuck method for simulating skewed stable random variables".
1230      * Statistics &amp; Probability Letters. 28 (2): 165–171.</a>
1231      */
1232     private static double sampleWeronAlphaNot1(double alpha, double beta, double w, double u) {
1233         // Update for symmetry around alpha = 1
1234         final double eps = 1 - alpha;
1235         final double meps1 = 1 - eps;
1236 
1237         double zeta;
1238         if (meps1 > 1) {
1239             zeta = beta * Math.tan((2 - meps1) * PI_2);
1240         } else {
1241             zeta = -beta * Math.tan(meps1 * PI_2);
1242         }
1243 
1244         final double scale = Math.pow(1 + zeta * zeta, 0.5 / meps1);
1245         final double invAlpha = 1.0 / meps1;
1246         final double invAlphaM1 = invAlpha - 1;
1247 
1248         final double phi = Math.PI * (u - 0.5);
1249 
1250         // Generic stable distribution.
1251 
1252         // Note: Adding back zeta creates the 0-parameterization defined in Nolan (1998):
1253         // X ~ S0_alpha(s,beta,u0) with s=1, u0=0 for a standard random variable.
1254         // As alpha -> 1 the translation zeta to create the stable deviate
1255         // in the 0-parameterization is increasingly large as tan(pi/2) -> infinity.
1256         // The max translation is approximately 1e16.
1257         // Without this translation the stable deviate is in the 1-parameterization
1258         // and the function is not continuous with respect to alpha.
1259         // Due to the large zeta when alpha -> 1 the number of bits of the output variable
1260         // are very low due to cancellation.
1261 
1262         // As alpha -> 0 or 2 then zeta -> 0 and cancellation is not relevant.
1263         // The formula can be modified for infinite terms to compute a result for extreme
1264         // deviates u and w when the CMS formula fails.
1265 
1266         // Note the following term is subject to floating point error:
1267         // final double xi = Math.atan(-zeta) / alpha;
1268         // final double alphaPhiXi = alpha * (phi + xi);
1269         // This is required: cos(phi - alphaPhiXi) > 0 => phi - alphaPhiXi in (-pi/2, pi/2).
1270         // Thus we compute atan(-zeta) and use it to compute two terms:
1271         // [1] alpha * (phi + xi) = alpha * (phi + atan(-zeta) / alpha) = alpha * phi + atan(-zeta)
1272         // [2] phi - alpha * (phi + xi) = phi - alpha * phi - atan(-zeta) = (1-alpha) * phi - atan(-zeta)
1273         final double atanZeta = Math.atan(-zeta);
1274 
1275         // Compute terms
1276         // Either term can be infinite or 0. Certain parameters compute 0 * inf.
1277         // t1=inf occurs alpha -> 0.
1278         // t1=0 occurs when beta = tan(-alpha * phi) / tan(alpha * pi / 2).
1279         // t2=inf occurs when w -> 0 and alpha -> 0.
1280         // t2=0 occurs when alpha -> 0 and phi -> pi/2.
1281         // Detect zeros and return as zeta.
1282 
1283         // Note sin(alpha * phi + atanZeta) is zero when:
1284         // alpha * phi = -atan(-zeta)
1285         // tan(-alpha * phi) = -zeta
1286         //                   = beta * tan(alpha * pi / 2)
1287         // Since |phi| < pi/2 this requires beta to have an opposite sign to phi
1288         // and a magnitude < 1. This is possible and in this case avoid a possible
1289         // 0 / 0 by setting the result as if term t1=0 and the result is zeta.
1290         double t1 = Math.sin(meps1 * phi + atanZeta);
1291         if (t1 == 0) {
1292             return zeta;
1293         }
1294         // Since cos(phi) is in (0, 1] this term will not create a
1295         // large magnitude to create t1 = 0.
1296         t1 /= Math.pow(Math.cos(phi), invAlpha);
1297 
1298         // Iff Math.cos(eps * phi - atanZeta) is zero then 0 / 0 can occur if w=0.
1299         // Iff Math.cos(eps * phi - atanZeta) is below zero then NaN will occur
1300         // in the power function. These cases are avoided by u=(0,1) and direct
1301         // use of arctan(-zeta).
1302         final double t2 = Math.pow(Math.cos(eps * phi - atanZeta) / w, invAlphaM1);
1303         if (t2 == 0) {
1304             return zeta;
1305         }
1306 
1307         return t1 * t2 * scale + zeta;
1308     }
1309 
1310     /**
1311      * Create a sample from a stable distribution. This is an implementation of the Weron
1312      * {@code alpha == 1} formula. The algorithm matches that
1313      * in the {@link Alpha1StableSampler} with the exception that
1314      * the uniform variate is provided in {@code (0, 1)}, not{@code (-pi/2, pi/2)}.
1315      *
1316      * @param alpha Stability parameter. Must be in the interval {@code (0, 2]}.
1317      * @param beta Skewness parameter. Must be in the interval {@code [-1, 1]}.
1318      * @param w Exponential variate
1319      * @param u Uniform variate
1320      * @return the sample
1321      * @see <a href="https://doi.org/10.1016%2F0167-7152%2895%2900113-1">Weron, R (1996).
1322      * "On the Chambers-Mallows-Stuck method for simulating skewed stable random variables".
1323      * Statistics &amp; Probability Letters. 28 (2): 165–171.</a>
1324      */
1325     private static double sampleWeronAlpha1(double beta, double w, double u) {
1326         // phi in (-pi/2, pi/2)
1327         final double phi = Math.PI * (u - 0.5);
1328 
1329         // Generic stable distribution with alpha = 1
1330         final double betaPhi = PI_2 + beta * phi;
1331         return (betaPhi * Math.tan(phi) -
1332                beta * Math.log(PI_2 * w * Math.cos(phi) / betaPhi)) / PI_2;
1333     }
1334 
1335     /*******************************/
1336     /* Tests for the StableSampler */
1337     /*******************************/
1338 
1339     /**
1340      * Test the general CMS sampler when the random generator outputs create
1341      * deviates that cause the value {@code z} to be negative.
1342      */
1343     @Test
1344     void testSamplesWithZBelow0() {
1345         // Call the CMS algorithm with u->1; phi/2 -> pi/4.
1346         // The value with all bits set generates phi/2 -> pi/4.
1347         // Add a long to create a big value for w of 5.
1348         // The parameters create cancellation in the numerator of z to create a negative z.
1349         final long[] longs = {Long.MAX_VALUE, -6261465550279131136L};
1350 
1351         final double phiby2 = PI_4 - PI_4 * DU;
1352         final double w = 5.0;
1353         assertUWSequence(new double[] {
1354             phiby2, w,
1355         }, longs);
1356 
1357         // The alpha parameter has been identified via a search with beta=-1.
1358         // See testZIsNotAlwaysAboveZero()
1359         final double alpha = 1.291015625;
1360         final double beta = -1;
1361         Assertions.assertTrue(0.0 > computeNumerator(alpha, beta, Long.MAX_VALUE));
1362 
1363         // z will be negative. Repeat computation assumed to be performed by the sampler.
1364         // This ensures the test should be updated if the sampler implementation changes.
1365         final double eps = 1 - alpha;
1366         final double tau = CMSStableSampler.getTau(alpha, beta);
1367         final double a = phiby2 * SpecialMath.tan2(phiby2);
1368         final double bb = SpecialMath.tan2(eps * phiby2);
1369         final double b = eps * phiby2 * bb;
1370         final double da = a * a;
1371         final double db = b * b;
1372         final double a2 = 1 - da;
1373         final double a2p = 1 + da;
1374         final double b2 = 1 - db;
1375         final double b2p = 1 + db;
1376         final double z = a2p * (b2 + 2 * phiby2 * bb * tau) / (w * a2 * b2p);
1377         Assertions.assertTrue(0.0 > z);
1378 
1379         final StableSampler sampler = StableSampler.of(createRngWithSequence(longs), alpha, beta);
1380         // It should not be NaN or infinite
1381         Assertions.assertTrue(Double.isFinite(sampler.sample()), "Sampler did not recover");
1382     }
1383 
1384     /**
1385      * Test the general CMS sampler when the random generator outputs create
1386      * deviates that cause the value {@code z} to be infinite.
1387      */
1388     @Test
1389     void testSamplesWithZInfinite() {
1390         // Call the CMS algorithm with w=0 (and phi/2 is not extreme).
1391         final long[] longs = {Long.MIN_VALUE >>> 1, 0};
1392 
1393         assertUWSequence(new double[] {
1394             PI_4 / 2, 0,
1395         }, longs);
1396 
1397         for (final double alpha : new double[] {0.789, 1, 1.23}) {
1398             // Test all directions
1399             for (final double beta : new double[] {-0.56, 0, 0.56}) {
1400                 // Ignore Cauchy case which does not use the exponential deviate
1401                 if (alpha == 1 && beta == 0) {
1402                     continue;
1403                 }
1404                 final StableSampler sampler = StableSampler.of(createRngWithSequence(longs), alpha, beta);
1405                 final double x = sampler.sample();
1406                 // It should not be NaN
1407                 Assertions.assertFalse(Double.isNaN(x), "Sampler did not recover");
1408                 if (beta != 0) {
1409                     // The sample is extreme so should be at a limit of the support
1410                     if (alpha < 0) {
1411                         // Effectively +/- infinity
1412                         Assertions.assertEquals(Math.copySign(Double.POSITIVE_INFINITY, beta), x);
1413                     } else if (alpha > 1) {
1414                         // At the distribution mean
1415                         final double[] support = getSupport(alpha, beta);
1416                         final double mu = support[2];
1417                         Assertions.assertEquals(mu, x);
1418                     }
1419                 }
1420             }
1421         }
1422     }
1423 
1424     /**
1425      * Test the CMS sampler when the random generator outputs create
1426      * deviates that cause the value {@code d} to be infinite.
1427      */
1428     @Test
1429     void testSamplesWithDInfinite() {
1430         // beta != 0 but with low skew to allow the direction switch in
1431         // phi/2 to create opposite directions.
1432         testSamplesWithDInfinite(0.01);
1433         testSamplesWithDInfinite(-0.01);
1434     }
1435 
1436     /**
1437      * Test the {@code beta=0} CMS sampler when the random generator outputs create
1438      * deviates that cause the value {@code d} to be infinite.
1439      */
1440     @Test
1441     void testBeta0SamplesWithDInfinite() {
1442         testSamplesWithDInfinite(0.0);
1443     }
1444 
1445     /**
1446      * Test the CMS sampler when the random generator outputs create deviates that
1447      * cause the value {@code d} to be infinite. This applies to the general sampler
1448      * or the sampler with {@code beta=0}.
1449      *
1450      * @param beta beta (should be close or equal to zero to allow direction changes due to the
1451      * angle phi/2 to be detected)
1452      */
1453     private static void testSamplesWithDInfinite(double beta) {
1454         // Set-up the random deviate u to be close to -pi/4 (low), pi/4 (high) and 0.5.
1455         // The extreme values for u create terms during error correction that are infinite.
1456         final long xuLo = Long.MIN_VALUE + (1024 << 10);
1457         final long xuHi = Long.MAX_VALUE - (1023 << 10);
1458         // Call sampler with smallest possible w that is not 0. This creates a finite z
1459         // but an infinite d due to the use of alpha -> 0.
1460         final long x = 3L;
1461         final long[] longs = {xuLo, x, xuHi, x, 0, x};
1462 
1463         assertUWSequence(new double[] {
1464             -PI_4 + 1024 * DU * PI_4, SMALL_W,
1465             PI_4 - 1024 * DU * PI_4, SMALL_W,
1466             0.0, SMALL_W
1467         }, longs);
1468 
1469         // alpha must be small to create infinite d and beta with low skew
1470         // to allow the direction switch in phi/2 to create opposite directions.
1471         // If the skew is too large then the skew dominates the direction.
1472         // When u=0.5 then f=0 and a standard sum of (1 + eps * d) * f + tau * d
1473         // with d=inf would cause inf * 0 = NaN.
1474         final double alpha = 0.03;
1475         final StableSampler sampler = StableSampler.of(createRngWithSequence(longs), alpha, beta);
1476         final double x1 = sampler.sample();
1477         final double x2 = sampler.sample();
1478         final double x3 = sampler.sample();
1479         // Expect the limit of the support (the direction is controlled by extreme phi)
1480         final double max = Double.POSITIVE_INFINITY;
1481         Assertions.assertEquals(-max, x1);
1482         Assertions.assertEquals(max, x2);
1483         // Expect the sampler to avoid inf * 0
1484         Assertions.assertNotEquals(Double.NaN, x3);
1485         // When f=0 the sample should be in the middle (beta=0) or skewed in the direction of beta
1486         if (beta == 0) {
1487             // In the middle
1488             Assertions.assertEquals(0.0, x3);
1489         } else {
1490             // At the support limit
1491             Assertions.assertEquals(Math.copySign(max, beta), x3);
1492         }
1493     }
1494 
1495     /**
1496      * Test the {@code alpha=1} CMS sampler when the random generator outputs create
1497      * deviates that cause the value {@code phi/2} to be at the extreme limits.
1498      */
1499     @Test
1500     void testAlpha1SamplesWithExtremePhi() {
1501         // The numerator is:
1502         // 1 + 2 * phiby2 * tau
1503         // tau = beta / pi/2 when alpha=1
1504         //     = +/-2 / pi when alpha=1, beta = +/-1
1505         // This should not create zero if phi/2 is not pi/4.
1506         // Test the limits of phi/2 to check samples are finite.
1507 
1508         // Add a long to create an ordinary value for w of 1.0.
1509         // u -> -pi/4
1510         final long[] longs1 = {Long.MIN_VALUE + (1 << 10), 2703662416942444033L};
1511         assertUWSequence(new double[] {
1512             -PI_4 + PI_4 * DU, 1.0,
1513         }, longs1);
1514         final StableSampler sampler1 = StableSampler.of(createRngWithSequence(longs1), 1.0, 1.0);
1515         final double x1 = sampler1.sample();
1516         Assertions.assertTrue(Double.isFinite(x1), "Sampler did not recover");
1517 
1518         // u -> pi/4
1519         final long[] longs2 = {Long.MAX_VALUE, 2703662416942444033L};
1520         assertUWSequence(new double[] {
1521             PI_4 - PI_4 * DU, 1.0,
1522         }, longs2);
1523         final StableSampler sampler2 = StableSampler.of(createRngWithSequence(longs2), 1.0, -1.0);
1524         final double x2 = sampler2.sample();
1525         Assertions.assertTrue(Double.isFinite(x2), "Sampler did not recover");
1526 
1527         // Sample should be a reflection
1528         Assertions.assertEquals(x1, -x2);
1529     }
1530 
1531     /**
1532      * Test the support of the distribution when {@code gamma = 1} and
1533      * {@code delta = 0}. A non-infinite support applies when {@code alpha < 0} and
1534      * {@code |beta| = 1}.
1535      */
1536     @Test
1537     void testSupport() {
1538         testSupport(1.0, 0.0);
1539     }
1540 
1541     /**
1542      * Test the support of the distribution when {@code gamma != 1} and
1543      * {@code delta != 0}. A non-infinite support applies when {@code alpha < 0} and
1544      * {@code |beta| = 1}.
1545      */
1546     @Test
1547     void testSupportWithTransformation() {
1548         // This tests extreme values which should not create NaN results
1549         for (final double gamma : new double[] {0.78, 1.23, Double.MAX_VALUE, Double.MIN_VALUE}) {
1550             for (final double delta : new double[] {0.43, 12.34, Double.MAX_VALUE}) {
1551                 testSupport(gamma, delta);
1552                 testSupport(gamma, -delta);
1553             }
1554         }
1555     }
1556 
1557     /**
1558      * Test the support of the distribution. This applies when {@code alpha < 0} and
1559      * {@code |beta| = 1}.
1560      *
1561      * @param alpha Stability parameter. Must be in range {@code (0, 2]}.
1562      * @param beta Skewness parameter. Must be in range {@code [-1, 1]}.
1563      * @param gamma Scale parameter. Must be strictly positive and finite.
1564      * @param delta Location parameter. Must be finite.
1565      */
1566     private static void testSupport(double gamma, double delta) {
1567         // When alpha is small (<=0.1) the computation becomes limited by floating-point precision.
1568         final double[] alphas = {2.0, 1.5, 1.0, Math.nextDown(1), 0.99, 0.75, 0.5, 0.25, 0.1, 0.01};
1569         for (final double alpha : alphas) {
1570             testSupport(alpha, 1, gamma, delta);
1571             testSupport(alpha, -1, gamma, delta);
1572         }
1573     }
1574     /**
1575      * Test the support of the distribution. This applies when {@code alpha < 0} and
1576      * {@code |beta| = 1}.
1577      *
1578      * @param alpha Stability parameter. Must be in range {@code (0, 2]}.
1579      * @param beta Skewness parameter. Must be in range {@code [-1, 1]}.
1580      * @param gamma Scale parameter. Must be strictly positive and finite.
1581      * @param delta Location parameter. Must be finite.
1582      */
1583     private static void testSupport(double alpha, double beta, double gamma, double delta) {
1584         // This is the inclusive bounds (no infinite values)
1585         final double[] support = getSupport(alpha, beta);
1586         // Do not scale the max value. It acts as an effective infinity.
1587         double lower;
1588         if (support[0] == -Double.MAX_VALUE) {
1589             lower = Double.NEGATIVE_INFINITY;
1590         } else {
1591             lower = support[0] * gamma + delta;
1592         }
1593         double upper;
1594         if (support[1] == Double.MAX_VALUE) {
1595             upper = Double.POSITIVE_INFINITY;
1596         } else {
1597             upper = support[1] * gamma + delta;
1598         }
1599         // Create an RNG that will generate extreme values:
1600         // Here we use 4 recursions into the tail of the exponential. The large exponential
1601         // deviate is approximately 30.3.
1602         final long[] longs = new long[] {
1603             // Note: Add a long of Long.MIN_VALUE to test the sampler ignores this value.
1604             // Hits edge case for generation of phi/4 in (-pi/4, pi/4)
1605             Long.MIN_VALUE,
1606 
1607             // phi/2 -> -pi/4, w=0
1608             Long.MIN_VALUE + (1 << 10), 0,
1609             // phi/2 -> -pi/4, w=large
1610             Long.MIN_VALUE + (1 << 10), -1, -1, -1, -1, -1, -1, -1, -1, 0,
1611             // phi/2 -> pi/4, w=0
1612             Long.MAX_VALUE, 0,
1613             // phi/2 -> pi/4, w=large
1614             Long.MAX_VALUE, -1, -1, -1, -1, -1, -1, -1, -1, 0,
1615             // phi/2=0, w=0
1616             0, 0,
1617             // phi/2=0, w=inf
1618             0, -1, -1, -1, -1, -1, -1, -1, -1, 0,
1619 
1620             // Add non extreme exponential deviate to test only extreme u
1621             // phi/2 -> -pi/4, w=1
1622             Long.MIN_VALUE + (1 << 10), 2703662416942444033L,
1623             // phi/2 -> pi/4, w=1
1624             Long.MAX_VALUE, 2703662416942444033L,
1625             // phi/2=0, w=1
1626             0, 2703662416942444033L,
1627 
1628             // Add non extreme uniform deviate to test only extreme w
1629             // phi/2=pi/5, w=0
1630             Long.MIN_VALUE >> 1, 0,
1631             // phi/2=pi/5, w=large
1632             Long.MIN_VALUE >> 1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
1633             // phi/2=pi/5, w=0
1634             Long.MIN_VALUE >>> 1, 0,
1635             // phi/2=pi/5, w=large
1636             Long.MIN_VALUE >>> 1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
1637         };
1638 
1639         // Validate series
1640         final double phiby2low = -PI_4 + PI_4 * DU;
1641         final double phiby2high = PI_4 - PI_4 * DU;
1642         assertUWSequence(new double[] {
1643             phiby2low, 0,
1644             phiby2low, LARGE_W,
1645             phiby2high, 0,
1646             phiby2high, LARGE_W,
1647             0, 0,
1648             0, LARGE_W,
1649             phiby2low, 1.0,
1650             phiby2high, 1.0,
1651             0, 1.0,
1652             -PI_4 / 2, 0,
1653             -PI_4 / 2, LARGE_W,
1654             PI_4 / 2, 0,
1655             PI_4 / 2, LARGE_W,
1656         }, longs);
1657 
1658         final StableSampler sampler = StableSampler.of(
1659             createRngWithSequence(longs), alpha, beta, gamma, delta);
1660         for (int i = 0; i < 100; i++) {
1661             final double x = sampler.sample();
1662             if (!(lower <= x && x <= upper)) {
1663                 Assertions.fail(String.format("Invalid sample. alpha=%s, beta=%s, gamma=%s, delta=%s [%s, %s] x=%s",
1664                     alpha, beta, gamma, delta, lower, upper, x));
1665             }
1666         }
1667     }
1668 
1669     /**
1670      * Gets the support of the distribution. This returns the inclusive bounds. So exclusive
1671      * infinity is computed as the maximum finite value. Compute the value {@code mu} which is the
1672      * mean of the distribution when {@code alpha > 1}.
1673      *
1674      * <pre>
1675      * x in [mu, +inf)    if alpha < 1, beta = 1
1676      * x in (-inf, mu]    if alpha < 1, beta = -1
1677      * x in (-inf, -inf)  otherwise
1678      * </pre>
1679      *
1680      * @param alpha the alpha
1681      * @param beta the beta
1682      * @return the support ({lower, upper, mu})
1683      */
1684     private static double[] getSupport(double alpha, double beta) {
1685         // Convert alpha as used by the sampler
1686         double eps = 1 - alpha;
1687         double meps1 = 1 - eps;
1688 
1689         // Since pi is approximate the symmetry is lost by wrapping.
1690         // Keep within the domain using (2-alpha).
1691         double mu;
1692         if (alpha > 1) {
1693             mu = beta * Math.tan((2 - meps1) * PI_2);
1694         } else {
1695             // Special case where tan(pi/4) is not 1 (it is Math.nextDown(1.0)).
1696             // This is needed when testing the Levy case during sampling.
1697             if (alpha == 0.5) {
1698                 mu = -beta;
1699             } else {
1700                 mu = -beta * Math.tan(meps1 * PI_2);
1701             }
1702         }
1703 
1704         // Standard support
1705         double lower = -Double.MAX_VALUE;
1706         double upper = Double.MAX_VALUE;
1707         if (meps1 < 1) {
1708             if (beta == 1) {
1709                 // alpha < 0, beta = 1
1710                 lower = mu;
1711             } else if (beta == -1) {
1712                 // alpha < 0, beta = -1
1713                 upper = mu;
1714             }
1715         }
1716         return new double[] {lower, upper, mu};
1717     }
1718 
1719     /**
1720      * Assumption test:
1721      * Test the random deviates u and w can be generated by manipulating the RNG.
1722      */
1723     @Test
1724     void testRandomDeviatesUandW() {
1725         // Extremes of the uniform deviate generated using the same method as the sampler
1726         final double d = DU * PI_4;
1727         // Test in (-pi/4, pi/4)
1728         Assertions.assertNotEquals(-PI_4, getU(createRngWithSequence(Long.MIN_VALUE)));
1729         Assertions.assertEquals(-PI_4 + d, getU(createRngWithSequence(Long.MIN_VALUE + (1 << 10))));
1730         Assertions.assertEquals(-PI_4 / 2, getU(createRngWithSequence(Long.MIN_VALUE >> 1)));
1731         Assertions.assertEquals(-d, getU(createRngWithSequence(-1)));
1732         Assertions.assertEquals(0.0, getU(createRngWithSequence(0)));
1733         Assertions.assertEquals(d, getU(createRngWithSequence(1 << 10)));
1734         Assertions.assertEquals(PI_4 / 2, getU(createRngWithSequence(Long.MIN_VALUE >>> 1)));
1735         Assertions.assertEquals(PI_4 - d, getU(createRngWithSequence(Long.MAX_VALUE)));
1736 
1737         // Extremes of the exponential sampler
1738         Assertions.assertEquals(0, ZigguratSampler.Exponential.of(
1739                 createRngWithSequence(0L)).sample());
1740         Assertions.assertEquals(SMALL_W, ZigguratSampler.Exponential.of(
1741                 createRngWithSequence(3)).sample());
1742         Assertions.assertEquals(0.5, ZigguratSampler.Exponential.of(
1743                 createRngWithSequence(1446480648965178882L)).sample());
1744         Assertions.assertEquals(1.0, ZigguratSampler.Exponential.of(
1745             createRngWithSequence(2703662416942444033L)).sample());
1746         Assertions.assertEquals(2.5, ZigguratSampler.Exponential.of(
1747                 createRngWithSequence(6092639261715210240L)).sample());
1748         Assertions.assertEquals(5.0, ZigguratSampler.Exponential.of(
1749             createRngWithSequence(-6261465550279131136L)).sample());
1750         Assertions.assertEquals(TAIL_W, ZigguratSampler.Exponential.of(
1751                 createRngWithSequence(-1, -1, 0)).sample());
1752         Assertions.assertEquals(3 * TAIL_W, ZigguratSampler.Exponential.of(
1753                 createRngWithSequence(-1, -1, -1, -1, -1, -1, 0)).sample(), 1e-14);
1754     }
1755 
1756     /**
1757      * Gets a uniform random variable in {@code (-pi/4, pi/4)}.
1758      *
1759      * <p>Copied from the StableSampler for testing. In the main sampler the variable u
1760      * is named either {@code phi} in {@code (-pi/2, pi/2)}, or
1761      * {@code phiby2} in {@code (-pi/4, pi/4)}. Here we test phiby2 for the CMS algorithm.
1762      *
1763      * @return u
1764      */
1765     private static double getU(UniformRandomProvider rng) {
1766         final double x = getU(rng.nextLong());
1767         if (x == -PI_4) {
1768             return getU(rng);
1769         }
1770         return x;
1771     }
1772 
1773     /**
1774      * Gets a uniform random variable in {@code [-pi/4, pi/4)} from a long value.
1775      *
1776      * <p>Copied from the StableSampler for testing. In the main sampler the variable u
1777      * is named either {@code phi} in {@code (-pi/2, pi/2)}, or
1778      * {@code phiby2} in {@code (-pi/4, pi/4)}. Here we test phiby2 for the CMS algorithm.
1779      *
1780      * <p>Examples of different output where {@code d} is the gap between values of {@code phi/2}
1781      * and is equal to {@code pi * 2^-55 = pi/4 * 2^-53}:
1782      *
1783      * <pre>
1784      * Long.MIN_VALUE                  -pi/4
1785      * Long.MIN_VALUE + (1 << 10)      -pi/4 + d
1786      * Long.MIN_VALUE >> 1             -pi/5
1787      * -1                              -d
1788      * 0                               0.0
1789      * 1 << 10                         d
1790      * Long.MIN_VALUE >>> 1            pi/5
1791      * Long.MAX_VALUE                  pi/4 - d
1792      * </pre>
1793      *
1794      * @return u
1795      */
1796     private static double getU(long x) {
1797         return (x >> 10) * PI_4_SCALED;
1798     }
1799 
1800     /**
1801      * Creates a RNG that will return the provided output for the next double and
1802      * next long functions. When the sequence is complete a valid random output
1803      * continues.
1804      *
1805      * <p>The sampler generates (in order):
1806      * <ol>
1807      * <li>{@code phi/2} in {@code (-pi/4, pi/4)} using long values from the RNG.
1808      * <li>{@code w} using the {@link ZigguratSampler.Exponential}.
1809      * This uses a long values from the RNG.
1810      * </ol>
1811      *
1812      * <p>Careful control of the the sequence can generate any value for {@code w} and {@code phi/2}.
1813      * The sampler creates a uniform deviate first, then an exponential deviate second.
1814      * Examples of different output where {@code d} is the gap between values of {@code phi/2}
1815      * and is equal to {@code pi * 2^-55 = pi/4 * 2^-53}:
1816      *
1817      * <pre>
1818      * longs                           phi/2               w
1819      * Long.MIN_VALUE                  try again [1]
1820      * Long.MIN_VALUE + (1 << 10)      -pi/4 + d
1821      * Long.MIN_VALUE >> 1             -pi/5
1822      * -1                              -d
1823      * 0                               0.0                 0
1824      * 1 << 10                         d
1825      * Long.MIN_VALUE >>> 1            pi/5
1826      * Long.MAX_VALUE                  pi/4 - d
1827      * 3                                                   6.564735882096453E-19
1828      * 1446480648965178882L                                0.5
1829      * 2703662416942444033L                                1.0
1830      * 6092639261715210240L                                2.5
1831      * -6261465550279131136L                               5.0
1832      * -1, -1, 0                                           7.569274694148063
1833      * -1L * 2n, 0                                         n * 7.569274694148063  [2]
1834      * </pre>
1835      *
1836      * <ol>
1837      * <li>When phi/2=-pi/4 the method will ignore the value and obtain another long value.
1838      * <li>To create a large value for the exponential sampler requires recursion. Each input
1839      * of 2 * -1L will add 7.569274694148063 to the total. A long of zero will stop recursion.
1840      * </ol>
1841      *
1842      * @param longs the initial sequence of longs
1843      * @return the uniform random provider
1844      */
1845     private static UniformRandomProvider createRngWithSequence(final long... longs) {
1846         // Note:
1847         // The StableSampler uniform deviate is generated from a long.
1848         // It is ignored if zero, a value of 1 << 11 generates the smallest value (2^-53).
1849         //
1850         // The ZigguratSampler.Exponential uses a single long value >98% of the time.
1851         // To create a certain value x the input y can be obtained by reversing the
1852         // computation of the corresponding precomputed factor X. The lowest 8 bits of y
1853         // choose the index i into X so must be set as the lowest bits.
1854         // The long is shifted right 1 before multiplying by X so this must be reversed.
1855         //
1856         // To find y to obtain the sample x use:
1857         // double[] X = { /* from ZigguratSampler.Exponential */ }
1858         // double x = 1.0; // or any other value < 7.5
1859         // for (int i = 0; i < X.length; i++) {
1860         //      // Add back the index to the lowest 8 bits.
1861         //      // This will work if the number is so big that the lower bits
1862         //      // are zerod when casting the 53-bit mantissa to a long.
1863         //      long y = ((long) (x / X[i]) << 1) + i;
1864         //      if ((y >>> 1) * X[i] == x) {
1865         //          // Found y!
1866         //      }
1867         // }
1868 
1869         // Start with a valid RNG.
1870         // This is required for nextDouble() since invoking super.nextDouble() when
1871         // the sequence has expired will call nextLong() and may use the intended
1872         // sequence of longs.
1873         final UniformRandomProvider rng = RandomAssert.seededRNG();
1874 
1875         // A RNG with the provided output
1876         return new SplitMix64(0L) {
1877             private int l;
1878 
1879             @Override
1880             public long nextLong() {
1881                 if (l == longs.length) {
1882                     return rng.nextLong();
1883                 }
1884                 return longs[l++];
1885             }
1886         };
1887     }
1888 
1889     /**
1890      * Assert the sequence of output from a uniform deviate and exponential deviate
1891      * created using the same method as the sampler.
1892      *
1893      * <p>The RNG is created using
1894      * {@link #createRngWithSequence(long[])}. See the method javadoc for
1895      * examples of how to generate different deviate values.
1896      *
1897      * @param expected the expected output (u1, w1, u2, w2, u3, w3, ...)
1898      * @param longs the initial sequence of longs
1899      */
1900     private static void assertUWSequence(double[] expected, long[] longs) {
1901         final UniformRandomProvider rng = createRngWithSequence(longs);
1902 
1903         // Validate series
1904         final SharedStateContinuousSampler exp = ZigguratSampler.Exponential.of(rng);
1905         for (int i = 0; i < expected.length; i += 2) {
1906             final int j = i / 2;
1907             Assertions.assertEquals(expected[i], getU(rng), () -> j + ": Incorrect u");
1908             if (i + 1 < expected.length) {
1909                 Assertions.assertEquals(expected[i + 1], exp.sample(), () -> j + ": Incorrect w");
1910             }
1911         }
1912     }
1913 
1914     /**
1915      * Test the sampler output is a continuous function of {@code alpha} and {@code beta}.
1916      * This test verifies the switch to the dedicated {@code alpha=1} or {@code beta=0}
1917      * samplers computes a continuous function of the parameters.
1918      */
1919     @Test
1920     void testSamplerOutputIsContinuousFunction() {
1921         // Test alpha passing through 1 when beta!=0 (switch to an alpha=1 sampler)
1922         for (final double beta : new double[] {0.5, 0.2, 0.1, 0.001}) {
1923             testSamplerOutputIsContinuousFunction(1 + 8096 * DU, beta, 1.0, beta, 1 - 8096 * DU, beta, 0);
1924             testSamplerOutputIsContinuousFunction(1 + 1024 * DU, beta, 1.0, beta, 1 - 1024 * DU, beta, 1);
1925             // Not perfect when alpha -> 1
1926             testSamplerOutputIsContinuousFunction(1 + 128 * DU, beta, 1.0, beta, 1 - 128 * DU, beta, 1);
1927             testSamplerOutputIsContinuousFunction(1 + 16 * DU, beta, 1.0, beta, 1 - 16 * DU, beta, 4);
1928             // This works with ulp=0. Either this is a lucky random seed or because the approach
1929             // to 1 creates equal output.
1930             testSamplerOutputIsContinuousFunction(1 + DU, beta, 1.0, beta, 1 - DU, beta, 0);
1931         }
1932         // Test beta passing through 0 when alpha!=1 (switch to a beta=0 sampler)
1933         for (final double alpha : new double[] {1.5, 1.2, 1.1, 1.001}) {
1934             testSamplerOutputIsContinuousFunction(alpha, 8096 * DU, alpha, 0, alpha, -8096 * DU, 0);
1935             testSamplerOutputIsContinuousFunction(alpha, 1024 * DU, alpha, 0, alpha, -1024 * DU, 0);
1936             testSamplerOutputIsContinuousFunction(alpha, 128 * DU, alpha, 0, alpha, -128 * DU, 1);
1937             // Not perfect when beta is very small
1938             testSamplerOutputIsContinuousFunction(alpha, 16 * DU, alpha, 0, alpha, -16 * DU, 64);
1939             testSamplerOutputIsContinuousFunction(alpha, DU, alpha, 0, alpha, -DU, 4);
1940         }
1941 
1942         // Note: No test for transition to the Cauchy case (alpha=1, beta=0).
1943         // Requires a RNG that discards output that would be used to create a exponential
1944         // deviate. Just create one each time a request for nextLong is performed and
1945         // ensure nextLong >>> 11 is not zero.
1946 
1947         // When the parameters create a special case sampler this will not work.
1948         // alpha passing through 0.5 when beta=1 (Levy sampler)
1949         // alpha -> 2 as the sampler (Gaussian sampler).
1950     }
1951 
1952     /**
1953      * Test sampler output is a continuous function of {@code alpha} and
1954      * {@code beta}. Create 3 samplers with the same RNG and test the middle sampler
1955      * computes a value between the upper and lower sampler.
1956      *
1957      * @param alpha1 lower sampler alpha
1958      * @param beta1 lower sampler beta
1959      * @param alpha2 middle sampler alpha
1960      * @param beta2 middle sampler beta
1961      * @param alpha3 upper sampler alpha
1962      * @param beta3 upper sampler beta
1963      */
1964     private static void testSamplerOutputIsContinuousFunction(double alpha1, double beta1,
1965                                                               double alpha2, double beta2,
1966                                                               double alpha3, double beta3,
1967                                                               int ulp) {
1968         final long seed = 0x62738468L;
1969         final UniformRandomProvider rng1 = RandomSource.XO_RO_SHI_RO_128_PP.create(seed);
1970         final UniformRandomProvider rng2 = RandomSource.XO_RO_SHI_RO_128_PP.create(seed);
1971         final UniformRandomProvider rng3 = RandomSource.XO_RO_SHI_RO_128_PP.create(seed);
1972         final StableSampler sampler1 = StableSampler.of(rng1, alpha1, beta1);
1973         final StableSampler sampler2 = StableSampler.of(rng2, alpha2, beta2);
1974         final StableSampler sampler3 = StableSampler.of(rng3, alpha3, beta3);
1975         final Supplier<String> msg = () -> String.format("alpha=%s, beta=%s", alpha2, beta2);
1976         for (int i = 0; i < 1000; i++) {
1977             final double x1 = sampler1.sample();
1978             final double x2 = sampler2.sample();
1979             final double x3 = sampler3.sample();
1980             // x2 should be in between x1 and x3
1981             if (x3 > x1) {
1982                 if (x2 > x3) {
1983                     // Should be the same
1984                     Assertions.assertEquals(x3, x2, ulp * Math.ulp(x3), msg);
1985                 } else if (x2 < x1) {
1986                     Assertions.assertEquals(x1, x2, ulp * Math.ulp(x1), msg);
1987                 }
1988             } else if (x3 < x1) {
1989                 if (x2 < x3) {
1990                     // Should be the same
1991                     Assertions.assertEquals(x3, x2, ulp * Math.ulp(x3), msg);
1992                 } else if (x2 > x1) {
1993                     Assertions.assertEquals(x1, x2, ulp * Math.ulp(x1), msg);
1994                 }
1995             }
1996         }
1997     }
1998 
1999     /**
2000      * Test the SharedStateSampler implementation for each case using a different implementation.
2001      */
2002     @Test
2003     void testSharedStateSampler() {
2004         // Gaussian case
2005         testSharedStateSampler(2.0, 0.0);
2006         // Cauchy case
2007         testSharedStateSampler(1.0, 0.0);
2008         // Levy case
2009         testSharedStateSampler(0.5, 1.0);
2010         // Hit code coverage of alpha=0.5 (Levy case) but beta != 1
2011         testSharedStateSampler(0.5, 0.1);
2012         // Beta 0 (symmetric) case
2013         testSharedStateSampler(1.3, 0.0);
2014         // Alpha 1 case
2015         testSharedStateSampler(1.0, 0.23);
2016         // Alpha close to 1
2017         testSharedStateSampler(Math.nextUp(1.0), 0.23);
2018         // General case
2019         testSharedStateSampler(1.3, 0.1);
2020         // Small alpha cases
2021         testSharedStateSampler(1e-5, 0.1);
2022         testSharedStateSampler(1e-5, 0.0);
2023         // Large alpha case.
2024         // This hits code coverage for computing tau from (1-alpha) -> -1
2025         testSharedStateSampler(1.99, 0.1);
2026     }
2027 
2028     /**
2029      * Test the SharedStateSampler implementation. This tests with and without the
2030      * {@code gamma} and {@code delta} parameters.
2031      *
2032      * @param alpha Alpha.
2033      * @param beta Beta.
2034      */
2035     private static void testSharedStateSampler(double alpha, double beta) {
2036         final UniformRandomProvider rng1 = RandomAssert.seededRNG();
2037         final UniformRandomProvider rng2 = RandomAssert.seededRNG();
2038         StableSampler sampler1 = StableSampler.of(rng1, alpha, beta);
2039         StableSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
2040         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
2041         // Test shifted
2042         sampler1 = StableSampler.of(rng1, alpha, beta, 1.3, 13.2);
2043         sampler2 = sampler1.withUniformRandomProvider(rng2);
2044         RandomAssert.assertProduceSameSequence(sampler1, sampler2);
2045     }
2046 
2047     /**
2048      * Test the implementation of the transformed sampler (scaled and translated).
2049      */
2050     @Test
2051     void testTransformedSampler() {
2052         // Gaussian case
2053         // The Gaussian case has its own scaling where the StdDev is gamma * sqrt(2).
2054         // (N(x) * sqrt(2)) * gamma != N(x) * (sqrt(2) * gamma)
2055         // Test with a delta
2056         testTransformedSampler(2.0, 0.0, 1);
2057         // Cauchy case
2058         testTransformedSampler(1.0, 0.0);
2059         // Levy case
2060         testTransformedSampler(0.5, 1.0);
2061         // Symmetric case
2062         testTransformedSampler(1.3, 0.0);
2063         // Alpha 1 case
2064         testTransformedSampler(1.0, 0.23);
2065         // Alpha close to 1
2066         testTransformedSampler(Math.nextUp(1.0), 0.23);
2067         // General case
2068         testTransformedSampler(1.3, 0.1);
2069         // Small alpha case
2070         testTransformedSampler(1e-5, 0.1);
2071         // Large alpha case.
2072         // This hits the case for computing tau from (1-alpha) -> -1.
2073         testTransformedSampler(1.99, 0.1);
2074     }
2075 
2076     /**
2077      * Test the implementation of the transformed sampler (scaled and translated).
2078      * The transformed output must match exactly.
2079      *
2080      * @param alpha Alpha.
2081      * @param beta Beta.
2082      */
2083     private static void testTransformedSampler(double alpha, double beta) {
2084         testTransformedSampler(alpha, beta, 0);
2085     }
2086 
2087     /**
2088      * Test the implementation of the transformed sampler (scaled and translated).
2089      * The transformed output must match within the provided ULP.
2090      *
2091      * @param alpha Alpha.
2092      * @param beta Beta.
2093      * @param ulp Allowed ULP difference.
2094      */
2095     private static void testTransformedSampler(double alpha, double beta, int ulp) {
2096         final UniformRandomProvider[] rngs = RandomAssert.createRNG(2);
2097         final UniformRandomProvider rng1 = rngs[0];
2098         final UniformRandomProvider rng2 = rngs[1];
2099         final double gamma = 3.4;
2100         final double delta = -17.3;
2101         final StableSampler sampler1 = StableSampler.of(rng1, alpha, beta, gamma, delta);
2102         final ContinuousSampler sampler2 = createTransformedSampler(rng2, alpha, beta, gamma, delta);
2103         if (ulp == 0) {
2104             RandomAssert.assertProduceSameSequence(sampler1, sampler2);
2105         } else {
2106             for (int i = 0; i < 10; i++) {
2107                 final double x1 = sampler1.sample();
2108                 final double x2 = sampler2.sample();
2109                 Assertions.assertEquals(x1, x2, ulp * Math.ulp(x1));
2110             }
2111         }
2112     }
2113 
2114     /**
2115      * Create a transformed sampler from a normalized sampler scaled and translated by
2116      * gamma and delta.
2117      *
2118      * @param rng Source of randomness.
2119      * @param alpha Alpha.
2120      * @param beta Beta.
2121      * @param gamma Gamma.
2122      * @param delta Delta.
2123      * @return the transformed sampler
2124      */
2125     private static ContinuousSampler createTransformedSampler(UniformRandomProvider rng,
2126                                                               double alpha, double beta,
2127                                                               final double gamma, final double delta) {
2128         final StableSampler delegate = StableSampler.of(rng, alpha, beta);
2129         return new ContinuousSampler() {
2130             @Override
2131             public double sample() {
2132                 return gamma * delegate.sample() + delta;
2133             }
2134         };
2135     }
2136 
2137     /**
2138      * Test symmetry when when u and beta are mirrored around 0.5 and 0 respectively.
2139      */
2140     @Test
2141     void testSymmetry() {
2142         final byte[] seed = RandomSource.KISS.createSeed();
2143         for (final double alpha : new double[] {1e-4, 0.78, 1, 1.23}) {
2144             for (final double beta : new double[] {-0.43, 0.23}) {
2145                 for (final double gamma : new double[] {0.78, 1, 1.23}) {
2146                     for (final double delta : new double[] {-0.43, 0, 0.23}) {
2147                         // The sampler generates u then w.
2148                         // If u is not -pi/4 then only a single long is used.
2149                         // This can be reversed around 0 by reversing the upper 54-bits.
2150                         // w will use 1 long only for fast lookup and then additional longs
2151                         // for edge of the ziggurat sampling. Fast look-up is always used
2152                         // when the lowest 8-bits create a value below 252.
2153 
2154                         // Use the same random source for two samplers.
2155                         final UniformRandomProvider rng1 = RandomSource.KISS.create(seed);
2156                         final UniformRandomProvider rng2 = RandomSource.KISS.create(seed);
2157 
2158                         // RNG which will not return 0 for every other long.
2159                         final UniformRandomProvider forward = new SplitMix64(0) {
2160                             private int i;
2161                             @Override
2162                             public long nextLong() {
2163                                 // Manipulate alternate longs
2164                                 if ((i++ & 0x1) == 0) {
2165                                     // This must not be Long.MIN_VALUE.
2166                                     // So set the lowest bit of the upper 54-bits.
2167                                     final long x = rng1.nextLong() >>> 10 | 1L;
2168                                     // Shift back
2169                                     return x << 10;
2170                                 }
2171                                 // For the exponential sample ensure the lowest 8-bits are < 252.
2172                                 long x;
2173                                 do {
2174                                     x = rng1.nextLong();
2175                                 } while ((x & 0xff) >= 252);
2176                                 return x;
2177                             }
2178                         };
2179 
2180                         // RNG which will not return 0 for every other long but this long is reversed.
2181                         final UniformRandomProvider reverse = new SplitMix64(0) {
2182                             private final long upper = 1L << 54;
2183                             private int i;
2184                             @Override
2185                             public long nextLong() {
2186                                 // Manipulate alternate longs
2187                                 if ((i++ & 0x1) == 0) {
2188                                     // This must not be Long.MIN_VALUE.
2189                                     // So set the lowest bit of the upper 54-bits.
2190                                     final long x = rng2.nextLong() >>> 10 | 1L;
2191                                     // Reverse then shift back
2192                                     return (upper - x) << 10;
2193                                 }
2194                                 // For the exponential sample ensure the lowest 8-bits are < 252.
2195                                 long x;
2196                                 do {
2197                                     x = rng2.nextLong();
2198                                 } while ((x & 0xff) >= 252);
2199                                 return x;
2200                             }
2201                         };
2202 
2203                         final StableSampler s1 = StableSampler.of(forward, alpha, beta, gamma, delta);
2204                         // Since mirroring applies before the shift of delta this must be negated too
2205                         final StableSampler s2 = StableSampler.of(reverse, alpha, -beta, gamma, -delta);
2206                         for (int i = 0; i < 100; i++) {
2207                             Assertions.assertEquals(s1.sample(), -s2.sample());
2208                         }
2209                     }
2210                 }
2211             }
2212         }
2213     }
2214 
2215     /**
2216      * Test symmetry for the Levy case ({@code alpha = 0.5} and {@code beta = 1}.
2217      */
2218     @Test
2219     void testSymmetryLevy() {
2220         final double alpha = 0.5;
2221         final double beta = 1.0;
2222         final byte[] seed = RandomSource.KISS.createSeed();
2223         final UniformRandomProvider rng1 = RandomSource.KISS.create(seed);
2224         final UniformRandomProvider rng2 = RandomSource.KISS.create(seed);
2225         for (final double gamma : new double[] {0.78, 1, 1.23}) {
2226             for (final double delta : new double[] {-0.43, 0, 0.23}) {
2227                 final StableSampler s1 = StableSampler.of(rng1, alpha, beta, gamma, delta);
2228                 // Since mirroring applies before the shift of delta this must be negated too
2229                 final StableSampler s2 = StableSampler.of(rng2, alpha, -beta, gamma, -delta);
2230                 for (int i = 0; i < 100; i++) {
2231                     Assertions.assertEquals(s1.sample(), -s2.sample());
2232                 }
2233             }
2234         }
2235     }
2236 
2237     /**
2238      * Test the toString method for cases not hit in the rest of the test suite.
2239      * This test asserts the toString method always contains the string 'stable'
2240      * even for parameters that create the Gaussian, Cauchy or Levy cases.
2241      */
2242     @Test
2243     void testToString() {
2244         final UniformRandomProvider rng = RandomAssert.seededRNG();
2245         for (final double[] p : new double[][] {
2246             {1.3, 0.1},
2247             {2.0, 0.0},
2248             {1.0, 0.0},
2249             {0.5, 1.0},
2250             {1e-5, 0},
2251             {1e-5, 0.1},
2252             {0.7, 0.1, 3.0, 4.5},
2253         }) {
2254             StableSampler sampler;
2255             if (p.length == 2) {
2256                 sampler = StableSampler.of(rng, p[0], p[1]);
2257             } else {
2258                 sampler = StableSampler.of(rng, p[0], p[1], p[2], p[3]);
2259             }
2260             final String s = sampler.toString().toLowerCase();
2261             Assertions.assertTrue(s.contains("stable"));
2262         }
2263     }
2264 
2265     /**
2266      * Demonstrate the CMS sampler matches the Weron sampler when {@code alpha != 1}.
2267      * This shows the two are equivalent; they should match as the formulas are rearrangements.
2268      * Avoid testing as {@code alpha -> 1} as the Weron sampler loses bits of precision in
2269      * the output sample.
2270      *
2271      * <p>Note: Uses direct instantiation via the package-private constructors. This avoids
2272      * the factory method constructor to directly select the implementation. Constructor
2273      * parameters are not validated.
2274      */
2275     @Test
2276     void testImplementationsMatch() {
2277         // Avoid extreme samples. Do this by manipulating the output of nextLong.
2278         // Generation of the random deviate u uses the top 54-bits of the long.
2279         // Unset a high bit to ensure getU cannot approach pi/4.
2280         // Set a low bit to ensure getU cannot approach -pi/4.
2281         final long unsetHighBit = ~(1L << 54);
2282         final long setLowBit = 1L << 53;
2283         final double hi = getU(Long.MAX_VALUE & unsetHighBit);
2284         final double lo = getU(Long.MIN_VALUE | setLowBit);
2285         // The limits are roughly pi/4 and -pi/4
2286         Assertions.assertEquals(PI_4, hi, 2e-3);
2287         Assertions.assertEquals(-PI_4, lo, 2e-3);
2288         Assertions.assertEquals(0.0, lo + hi, 1e-3);
2289 
2290         // Setting a bit ensure the exponential sampler cannot be zero
2291         final UniformRandomProvider rng = createRngWithSequence(setLowBit);
2292         final double w = ZigguratSampler.Exponential.of(rng).sample();
2293         Assertions.assertNotEquals(0.0, w);
2294         // This is the actual value; it is small but not extreme.
2295         Assertions.assertEquals(0.0036959349092519837, w);
2296 
2297         final RandomSource source = RandomSource.XO_RO_SHI_RO_128_SS;
2298         final long seed = 0x83762b3daf1c43L;
2299         final UniformRandomProvider rng1 = new SplitMix64(0L) {
2300             private UniformRandomProvider delegate = source.create(seed);
2301             @Override
2302             public long next() {
2303                 final long x = delegate.nextLong();
2304                 return (x & unsetHighBit) | setLowBit;
2305             }
2306         };
2307         final UniformRandomProvider rng2 = new SplitMix64(0L) {
2308             private UniformRandomProvider delegate = source.create(seed);
2309             @Override
2310             public long next() {
2311                 final long x = delegate.nextLong();
2312                 return (x & unsetHighBit) | setLowBit;
2313             }
2314         };
2315 
2316         // Not too close to alpha=1
2317         final double[] alphas = {0.3, 0.5, 1.2, 1.5};
2318         final double[] betas = {-0.5, -0.3, -0.1, 0};
2319 
2320         final double relative = 1e-5;
2321         final double absolute = 1e-10;
2322 
2323         for (final double alpha : alphas) {
2324             for (final double beta : betas) {
2325                 final Supplier<String> msg = () -> String.format("alpha=%s, beta=%s", alpha, beta);
2326                 // WARNING:
2327                 // Created by direct access to package-private constructor.
2328                 // This is for testing only as these do not validate the parameters.
2329                 StableSampler s1;
2330                 StableSampler s2;
2331                 if (beta == 0) {
2332                     s1 = new Beta0CMSStableSampler(rng1, alpha);
2333                     s2 = new Beta0WeronStableSampler(rng2, alpha);
2334                 } else {
2335                     s1 = new CMSStableSampler(rng1, alpha, beta);
2336                     s2 = new WeronStableSampler(rng2, alpha, beta);
2337                 }
2338                 for (int i = 0; i < 1000; i++) {
2339                     final double x = s1.sample();
2340                     final double y = s2.sample();
2341                     Assertions.assertEquals(x, y, Math.max(absolute, Math.abs(x) * relative), msg);
2342                 }
2343             }
2344         }
2345     }
2346 
2347     /**
2348      * Demonstrate the general CMS sampler matches the {@code beta = 0} sampler.
2349      * The {@code beta = 0} sampler implements the same algorithm with cancelled terms removed.
2350      *
2351      * <p>Note: Uses direct instantiation via the package-private constructors. This avoids
2352      * the factory method constructor to directly select the implementation. Constructor
2353      * parameters are not validated.
2354      */
2355     @Test
2356     void testSpecializedBeta0CMSImplementation() {
2357         final RandomSource source = RandomSource.XO_RO_SHI_RO_128_SS;
2358         // Should be robust to any seed
2359         final byte[] seed = source.createSeed();
2360         final UniformRandomProvider rng1 = source.create(seed);
2361         final UniformRandomProvider rng2 = source.create(seed);
2362 
2363         final double[] alphas = {0.3, 0.5, 1.2, 1.5};
2364         for (final double alpha : alphas) {
2365             // WARNING:
2366             // Created by direct access to package-private constructor.
2367             // This is for testing only as these do not validate the parameters.
2368             final StableSampler sampler1 = new CMSStableSampler(rng1, alpha, 0.0);
2369             final StableSampler sampler2 = new Beta0CMSStableSampler(rng2, alpha);
2370             RandomAssert.assertProduceSameSequence(sampler1, sampler2);
2371         }
2372     }
2373 
2374     /**
2375      * Demonstrate the general Weron sampler matches the {@code beta = 0} sampler.
2376      * The {@code beta = 0} sampler implements the same algorithm with cancelled terms removed.
2377      *
2378      * <p>Note: Uses direct instantiation via the package-private constructors. This avoids
2379      * the factory method constructor to directly select the implementation. Constructor
2380      * parameters are not validated.
2381      */
2382     @Test
2383     void testSpecializedBeta0WeronImplementation() {
2384         final RandomSource source = RandomSource.XO_RO_SHI_RO_128_SS;
2385         // Should be robust to any seed
2386         final byte[] seed = source.createSeed();
2387         final UniformRandomProvider rng1 = source.create(seed);
2388         final UniformRandomProvider rng2 = source.create(seed);
2389 
2390         final double[] alphas = {0.3, 0.5, 1.2, 1.5};
2391         for (final double alpha : alphas) {
2392             // WARNING:
2393             // Created by direct access to package-private constructor.
2394             // This is for testing only as these do not validate the parameters.
2395             final StableSampler sampler1 = new WeronStableSampler(rng1, alpha, 0.0);
2396             final StableSampler sampler2 = new Beta0WeronStableSampler(rng2, alpha);
2397             RandomAssert.assertProduceSameSequence(sampler1, sampler2);
2398         }
2399     }
2400 
2401     /**
2402      * Test the Weron sampler when the term t1 is zero in the numerator.
2403      * This hits an edge case where sin(alpha * phi + atan(-zeta)) is zero.
2404      *
2405      * @see #testSinAlphaPhiMinusAtanZeta()
2406      */
2407     @Test
2408     void testWeronImplementationEdgeCase() {
2409         double alpha = 0.25;
2410         // Solved in testSinAlphaPhiMinusAtanZeta()
2411         double beta = -0.48021693505171;
2412         // Require phi = PI_4.
2413         // This is the equivalent of phi/2 = pi/5
2414         final long x = Long.MIN_VALUE >>> 1;
2415         final long[] longs = new long[] {
2416             // phi/2=pi/5, w=0
2417             x, 0,
2418             // phi/2=pi/5, w=large
2419             x, -1, -1, -1, -1, -1, -1, -1, -1, 0,
2420             // phi/2=pi/5, w=1
2421             x, 2703662416942444033L,
2422         };
2423 
2424         // Validate series
2425         assertUWSequence(new double[] {
2426             PI_4 / 2, 0,
2427             PI_4 / 2, LARGE_W,
2428             PI_4 / 2, 1.0,
2429         }, longs);
2430 
2431         final double zeta = -beta * Math.tan(alpha * PI_2);
2432         Assertions.assertEquals(0.0, alpha * PI_4 + Math.atan(-zeta));
2433 
2434         final UniformRandomProvider rng = createRngWithSequence(longs);
2435         final StableSampler sampler = new WeronStableSampler(rng, alpha, beta);
2436         // zeta is the offset used to shift the 1-parameterization to the
2437         // 0-parameterization. This is returned when other terms multiply to zero.
2438         Assertions.assertEquals(zeta, sampler.sample());
2439         Assertions.assertEquals(zeta, sampler.sample());
2440         Assertions.assertEquals(zeta, sampler.sample());
2441     }
2442 }