1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.numbers.gamma;
18
19 import java.io.IOException;
20 import java.math.BigDecimal;
21 import java.util.Arrays;
22 import java.util.function.DoubleBinaryOperator;
23 import java.util.function.DoubleUnaryOperator;
24 import org.apache.commons.numbers.gamma.BoostGamma.Lanczos;
25 import org.junit.jupiter.api.Assertions;
26 import org.junit.jupiter.api.MethodOrderer;
27 import org.junit.jupiter.api.Order;
28 import org.junit.jupiter.api.Test;
29 import org.junit.jupiter.api.TestMethodOrder;
30 import org.junit.jupiter.params.ParameterizedTest;
31 import org.junit.jupiter.params.provider.CsvSource;
32 import org.junit.jupiter.params.provider.EnumSource;
33 import org.junit.jupiter.params.provider.EnumSource.Mode;
34 import org.junit.jupiter.params.provider.ValueSource;
35
36
37
38
39
40
41
42
43 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
44 class BoostGammaTest {
45
46 private static final double[] FACTORIAL = BoostGamma.getFactorials();
47
48 private static final double LANCZOS_THRESHOLD = 20;
49
50
51 private static final double ROOT_EPSILON = 1.4901161193847656E-8;
52
53
54 private static final int LOG_MAX_VALUE = 709;
55
56
57
58
59 private static final int LOG_MIN_VALUE = -708;
60
61
62 private static final int MAX_FACTORIAL = 170;
63
64 private static final double EULER = 0.5772156649015328606065120900824024310;
65
66
67 private static final DoubleDoubleBiPredicate USE_ASYM_APPROX =
68 (a, x) -> (x > 1000) && ((a < x) || (Math.abs(a - 50) / x < 1));
69
70 private static final DoubleDoubleBiPredicate NO_ASYM_APPROX = (a, x) -> false;
71
72 private static final DoubleDoubleBiPredicate ASYM_APPROX = (a, x) -> true;
73
74
75 private interface DoubleDoubleBiPredicate {
76
77
78
79
80
81 boolean test(double a, double b);
82 }
83
84
85 private interface TestError {
86
87
88
89 double getTolerance();
90
91
92
93
94 double getRmsTolerance();
95
96
97
98
99
100
101
102 static TestError of(String name, double tolerance, double rmsTolerance) {
103 return new TestError() {
104 @Override
105 public String toString() {
106 return name;
107 }
108
109 @Override
110 public double getTolerance() {
111 return tolerance;
112 }
113
114 @Override
115 public double getRmsTolerance() {
116 return rmsTolerance;
117 }
118 };
119 }
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 private enum TestCase implements TestError {
141
142
143
144
145
146
147
148
149
150 TGAMMAO_NEAR_0(BoostGammaTest::tgammaOriginal, "gamma_near_0_data.csv", 3.3, 1.2),
151
152 TGAMMAO_NEAR_1(BoostGammaTest::tgammaOriginal, "gamma_near_1_data.csv", 3.3, 1.2),
153
154 TGAMMAO_NEAR_2(BoostGammaTest::tgammaOriginal, "gamma_near_2_data.csv", 4.8, 1.3),
155
156 TGAMMAO_NEAR_M10(BoostGammaTest::tgammaOriginal, "gamma_near_m10_data.csv", 2.5, 1.2),
157
158 TGAMMAO_M20_0(BoostGammaTest::tgammaOriginal, "gamma_m20_0_data.csv", 4.5, 1.4),
159
160 TGAMMAO_0_20(BoostGammaTest::tgammaOriginal, "gamma_0_20_data.csv", 3.2, 1.2),
161
162 TGAMMAO_VERY_NEAR_0(BoostGammaTest::tgammaOriginal, "gamma_very_near_0_data.csv", 3.3, 0.75),
163
164
165 TGAMMA_FACTORIALS(BoostGamma::tgamma, "gamma_factorials_data.csv", 2.5, 0.8),
166
167 TGAMMA_NEAR_0(BoostGamma::tgamma, "gamma_near_0_data.csv", 1.6, 0.7),
168
169 TGAMMA_NEAR_1(BoostGamma::tgamma, "gamma_near_1_data.csv", 1.3, 0.7),
170
171 TGAMMA_NEAR_2(BoostGamma::tgamma, "gamma_near_2_data.csv", 1.1, 0.6),
172
173 TGAMMA_NEAR_M10(BoostGamma::tgamma, "gamma_near_m10_data.csv", 1.8, 0.7),
174
175 TGAMMA_NEAR_M55(BoostGamma::tgamma, "gamma_near_m55_data.csv", 2.5, 1.2),
176
177 TGAMMA_M20_0(BoostGamma::tgamma, "gamma_m20_0_data.csv", 3, 0.8),
178
179 TGAMMA_0_20(BoostGamma::tgamma, "gamma_0_20_data.csv", 2, 0.65),
180
181 TGAMMA_20_150(BoostGamma::tgamma, "gamma_20_150_data.csv", 3.8, 1.2),
182
183 TGAMMA_150_171(BoostGamma::tgamma, "gamma_150_171_data.csv", 3.2, 1.2),
184
185 TGAMMA_VERY_NEAR_0(BoostGamma::tgamma, "gamma_very_near_0_data.csv", 3.8, 0.7),
186
187
188 LGAMMA_FACTORIALS(BoostGamma::lgamma, "gamma_factorials_data.csv", 2, 0.8, 0.125),
189
190 LGAMMA_NEAR_0(BoostGamma::lgamma, "gamma_near_0_data.csv", 2, 1.2, 0.5),
191
192 LGAMMA_NEAR_1(BoostGamma::lgamma, "gamma_near_1_data.csv", 2, 1.5, 0.7),
193
194 LGAMMA_NEAR_2(BoostGamma::lgamma, "gamma_near_2_data.csv", 2, 0.7, 0.2),
195
196 LGAMMA_NEAR_M10(BoostGamma::lgamma, "gamma_near_m10_data.csv", 2, 3, 0.99),
197
198 LGAMMA_NEAR_M55(BoostGamma::lgamma, "gamma_near_m55_data.csv", 2, 0.9, 0.45),
199
200
201 LGAMMA_M20_0(BoostGamma::lgamma, "gamma_m20_0_data.csv", 2, 100, 9),
202
203 LGAMMA_0_20(BoostGamma::lgamma, "gamma_0_20_data.csv", 2, 0.95, 0.25),
204
205 LGAMMA_20_150(BoostGamma::lgamma, "gamma_20_150_data.csv", 2, 1.5, 0.45),
206
207 LGAMMA_150_171(BoostGamma::lgamma, "gamma_150_171_data.csv", 2, 1.6, 0.65),
208
209 LGAMMA_VERY_NEAR_0(BoostGamma::lgamma, "gamma_very_near_0_data.csv", 2, 1.8, 0.4),
210
211
212 TGAMMAP1M1(BoostGamma::tgamma1pm1, "gamma1pm1_data.csv", 1.8, 0.6),
213
214
215 LOG1PMX(SpecialMath::log1pmx, "log1pmx_data.csv", -0.9, 0.15);
216
217
218 private final DoubleUnaryOperator fun;
219
220
221 private final String filename;
222
223
224 private final int expected;
225
226
227 private final double maxUlp;
228
229
230 private final double rmsUlp;
231
232
233
234
235
236
237
238
239
240 TestCase(DoubleUnaryOperator fun, String filename, double maxUlp, double rmsUlp) {
241 this(fun, filename, 1, maxUlp, rmsUlp);
242 }
243
244
245
246
247
248
249
250
251
252
253 TestCase(DoubleUnaryOperator fun, String filename, int expected, double maxUlp, double rmsUlp) {
254 this.fun = fun;
255 this.filename = filename;
256 this.expected = expected;
257 this.maxUlp = maxUlp;
258 this.rmsUlp = rmsUlp;
259 }
260
261
262
263
264 DoubleUnaryOperator getFunction() {
265 return fun;
266 }
267
268
269
270
271 String getFilename() {
272 return filename;
273 }
274
275
276
277
278 int getExpectedField() {
279 return expected;
280 }
281
282 @Override
283 public double getTolerance() {
284 return maxUlp;
285 }
286
287 @Override
288 public double getRmsTolerance() {
289 return rmsUlp;
290 }
291 }
292
293
294
295
296
297
298 private enum BiTestCase implements TestError {
299
300 POWM1(BoostMath::powm1, "powm1_data.csv", 2.6, 0.4),
301
302 IGAMMA_UPPER_INT(BoostGamma::tgamma, "igamma_int_data.csv", 6, 1.5),
303
304 IGAMMA_UPPER_SMALL(BoostGamma::tgamma, "igamma_small_data.csv", 2.5, 0.9),
305
306 IGAMMA_UPPER_MED(BoostGamma::tgamma, "igamma_med_data.csv", 9, 1.85),
307
308 IGAMMA_UPPER_BIG(BoostGamma::tgamma, "igamma_big_data.csv", 8, 1.3),
309
310 IGAMMA_UPPER_EXTRA(BoostGamma::tgamma, "igamma_extra_data.csv", 13, 4),
311
312 IGAMMA_Q_INT(BoostGamma::gammaQ, "igamma_int_data.csv", 3, 5, 1.3),
313
314 IGAMMA_Q_SMALL(BoostGamma::gammaQ, "igamma_small_data.csv", 3, 4, 1.1),
315
316 IGAMMA_Q_MED(BoostGamma::gammaQ, "igamma_med_data.csv", 3, 6, 0.95),
317
318 IGAMMA_Q_BIG(BoostGamma::gammaQ, "igamma_big_data.csv", 3, 550, 62),
319
320 IGAMMA_Q_EXTRA(BoostGamma::gammaQ, "igamma_extra_data.csv", 3, 60, 18),
321
322 IGAMMA_LOWER_INT(BoostGamma::tgammaLower, "igamma_int_data.csv", 4, 6, 1.3),
323
324 IGAMMA_LOWER_SMALL(BoostGamma::tgammaLower, "igamma_small_data.csv", 4, 1.6, 0.55),
325
326 IGAMMA_LOWER_MED(BoostGamma::tgammaLower, "igamma_med_data.csv", 4, 6.5, 1.3),
327
328 IGAMMA_LOWER_BIG(BoostGamma::tgammaLower, "igamma_big_data.csv", 4, 8, 1.3),
329
330 IGAMMA_LOWER_EXTRA(BoostGamma::tgammaLower, "igamma_extra_data.csv", 4, 5, 1.4),
331
332 IGAMMA_P_INT(BoostGamma::gammaP, "igamma_int_data.csv", 5, 3.5, 0.95),
333
334 IGAMMA_P_SMALL(BoostGamma::gammaP, "igamma_small_data.csv", 5, 2.8, 0.9),
335
336 IGAMMA_P_MED(BoostGamma::gammaP, "igamma_med_data.csv", 5, 5, 0.9),
337
338 IGAMMA_P_BIG(BoostGamma::gammaP, "igamma_big_data.csv", 5, 430, 55),
339
340 IGAMMA_P_EXTRA(BoostGamma::gammaP, "igamma_extra_data.csv", 5, 0.7, 0.2),
341
342 GAMMA_P_DERIV_INT(BoostGamma::gammaPDerivative, "igamma_int_data_p_derivative.csv", 3.5, 1.1),
343
344 GAMMA_P_DERIV_SMALL(BoostGamma::gammaPDerivative, "igamma_small_data_p_derivative.csv", 3.3, 0.99),
345
346 GAMMA_P_DERIV_MED(BoostGamma::gammaPDerivative, "igamma_med_data_p_derivative.csv", 5, 1.25),
347
348 GAMMA_P_DERIV_BIG(BoostGamma::gammaPDerivative, "igamma_big_data_p_derivative.csv", 550, 55),
349
350 LOG_GAMMA_P_DERIV1_INT(BoostGammaTest::logGammaPDerivative1, "igamma_int_data_p_derivative.csv", 3, 50, 10),
351
352 LOG_GAMMA_P_DERIV1_SMALL(BoostGammaTest::logGammaPDerivative1, "igamma_small_data_p_derivative.csv", 3, 8e11, 5e10),
353
354 LOG_GAMMA_P_DERIV1_MED(BoostGammaTest::logGammaPDerivative1, "igamma_med_data_p_derivative.csv", 3, 190, 35),
355
356 LOG_GAMMA_P_DERIV1_BIG(BoostGammaTest::logGammaPDerivative1, "igamma_big_data_p_derivative.csv", 3, 1.2e6, 125000),
357
358 LOG_GAMMA_P_DERIV2_INT(BoostGammaTest::logGammaPDerivative2, "igamma_int_data_p_derivative.csv", 3, 2, 0.5),
359
360 LOG_GAMMA_P_DERIV2_SMALL(BoostGammaTest::logGammaPDerivative2, "igamma_small_data_p_derivative.csv", 3, 1.8e10, 1.4e9),
361
362 LOG_GAMMA_P_DERIV2_MED(BoostGammaTest::logGammaPDerivative2, "igamma_med_data_p_derivative.csv", 3, 18, 0.8),
363
364 LOG_GAMMA_P_DERIV2_BIG(BoostGammaTest::logGammaPDerivative2, "igamma_big_data_p_derivative.csv", 3, 40000, 3000),
365
366 IGAMMA_LARGE_X_ASYMP_TERM(BoostGammaTest::incompleteTgammaLargeX, "igamma_asymptotic_data.csv", 300, 110),
367
368 IGAMMA_LARGE_X_Q(BoostGammaTest::igammaQLargeXNoAsym, "igamma_asymptotic_data.csv", 3, 550, 130),
369
370 IGAMMA_LARGE_X_P(BoostGammaTest::igammaPLargeXNoAsym, "igamma_asymptotic_data.csv", 4, 1, 0.3),
371
372 IGAMMA_LARGE_X_Q_ASYM(BoostGammaTest::igammaQLargeXWithAsym, "igamma_asymptotic_data.csv", 3, 550, 190),
373
374 IGAMMA_LARGE_X_P_ASYM(BoostGammaTest::igammaPLargeXWithAsym, "igamma_asymptotic_data.csv", 4, 350, 110),
375
376 TGAMMA_DELTA_RATIO(BoostGamma::tgammaDeltaRatio, "gamma_delta_ratio_data.csv", 2, 9.5, 2.1),
377
378 TGAMMA_DELTA_RATIO_SMALL_INT(BoostGamma::tgammaDeltaRatio, "gamma_delta_ratio_int_data.csv", 2, 4.7, 1.1),
379
380 TGAMMA_DELTA_RATIO_INT(BoostGamma::tgammaDeltaRatio, "gamma_delta_ratio_int2_data.csv", 2, 1.4, 0.45),
381
382 TGAMMA_DELTA_RATIO_NEG(BoostGammaTest::tgammaDeltaRatioNegative, "gamma_delta_ratio_data.csv", 3, 11.5, 1.7),
383
384 TGAMMA_DELTA_RATIO_SMALL_INT_NEG(BoostGammaTest::tgammaDeltaRatioNegative, "gamma_delta_ratio_int_data.csv", 3, 3.6, 1),
385
386 TGAMMA_DELTA_RATIO_INT_NEG(BoostGammaTest::tgammaDeltaRatioNegative, "gamma_delta_ratio_int2_data.csv", 3, 1.1, 0.25),
387
388 TGAMMA_RATIO(BoostGamma::tgammaDeltaRatio, "gamma_delta_ratio_data.csv", 9.5, 2);
389
390
391 private final DoubleBinaryOperator fun;
392
393
394 private final String filename;
395
396
397 private final int expected;
398
399
400 private final double maxUlp;
401
402
403 private final double rmsUlp;
404
405
406
407
408
409
410
411
412
413 BiTestCase(DoubleBinaryOperator fun, String filename, double maxUlp, double rmsUlp) {
414 this(fun, filename, 2, maxUlp, rmsUlp);
415 }
416
417
418
419
420
421
422
423
424
425
426 BiTestCase(DoubleBinaryOperator fun, String filename, int expected, double maxUlp, double rmsUlp) {
427 this.fun = fun;
428 this.filename = filename;
429 this.expected = expected;
430 this.maxUlp = maxUlp;
431 this.rmsUlp = rmsUlp;
432 }
433
434
435
436
437 DoubleBinaryOperator getFunction() {
438 return fun;
439 }
440
441
442
443
444 String getFilename() {
445 return filename;
446 }
447
448
449
450
451 int getExpectedField() {
452 return expected;
453 }
454
455 @Override
456 public double getTolerance() {
457 return maxUlp;
458 }
459
460 @Override
461 public double getRmsTolerance() {
462 return rmsUlp;
463 }
464 }
465
466 @ParameterizedTest
467 @CsvSource({
468
469 "0, NaN",
470 "-1, NaN",
471 "-2, NaN",
472
473 "1, 1",
474 "2, 1",
475 "3, 2",
476 "4, 6",
477 "5, 24",
478 "171, 0.7257415615307998967396728211129263114717e307",
479 "171.9, Infinity",
480 "172, Infinity",
481 "172.1, Infinity",
482 "500.34, Infinity, 0",
483 "1000.123, Infinity, 0",
484 "2000.1, Infinity, 0",
485
486 "-171.99999999999997, 0.0",
487 "-172, NaN",
488 "-172.00000000000003, -0.0",
489 "-172.99999999999997, -0.0",
490 "-173, NaN",
491 "-173.00000000000003, 0.0",
492 })
493 void testTGammaEdgeCases(double z, double expected) {
494 Assertions.assertEquals(expected, BoostGamma.tgamma(z));
495 }
496
497
498
499
500
501 @ParameterizedTest
502 @CsvSource({
503
504 "171.5, 9.483367566824799e+307, 1",
505 "171.62, 1.7576826789978127e+308, 1",
506 "171.624, 1.7942117599248106e+308, 4",
507 "171.62437600000001, 1.7976842943982607e+308, 4",
508 "171.6244, Infinity, 0",
509 })
510 void testTGammaLimit(double z, double expected, int ulp) {
511 TestUtils.assertEquals(expected, BoostGamma.tgamma(z), ulp);
512 }
513
514
515
516
517
518 @Test
519 void testTGammaSpotTests() {
520 final int tolerance = 50;
521 assertClose(BoostGamma::tgamma, 3.5, 3.3233509704478425511840640312646472177454052302295, tolerance);
522 assertClose(BoostGamma::tgamma, 0.125, 7.5339415987976119046992298412151336246104195881491, tolerance);
523 assertClose(BoostGamma::tgamma, -0.125, -8.7172188593831756100190140408231437691829605421405, tolerance);
524 assertClose(BoostGamma::tgamma, -3.125, 1.1668538708507675587790157356605097019141636072094, tolerance);
525
526 assertClose(BoostGamma::tgamma, -53249.0 / 1024, -1.2646559519067605488251406578743995122462767733517e-65, tolerance * 3);
527
528
529 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -12), 4095.42302574977164107280305038926932586783813167844235368772, tolerance);
530 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -14), 16383.4228446989052821887834066513143241996925504706815681204, tolerance * 2);
531 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -25), 3.35544314227843645746319656372890833248893111091576093784981e7, tolerance);
532 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -27), 1.34217727422784342467508497080056807355928046680073490038257e8, tolerance);
533 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -29), 5.36870911422784336940727488260481582524683632281496706906706e8, tolerance);
534 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -35), 3.43597383674227843351272524573929605605651956475300480712955e10, tolerance);
535 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -54), 1.80143985094819834227843350984671942971248427509141008005685e16, tolerance);
536 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -64), 1.84467440737095516154227843350984671394471047428598176073616e19, tolerance);
537 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -66), 7.37869762948382064634227843350984671394068921181531525785592922800e19, tolerance);
538 assertClose(BoostGamma::tgamma, Math.scalb(1.0, -33), 8.58993459142278433521360841138215453639282914047157884932317481977e9, tolerance);
539 assertClose(BoostGamma::tgamma, 4 / Double.MAX_VALUE, Double.MAX_VALUE / 4, tolerance);
540 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -12), -4096.57745718775464971331294488248972086965434176847741450728, tolerance);
541 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -14), -16384.5772760354695939336148831283410381037202353359487504624, tolerance * 2);
542 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -25), -3.35544325772156943776992988569766723938420508937071533029983e7, tolerance);
543 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -27), -1.34217728577215672270574319043497450577151370942651414968627e8, tolerance);
544 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -29), -5.36870912577215666743793215770406791630514293641886249382012e8, tolerance);
545 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -34), -1.71798691845772156649591034966100693794360502123447124928244e10, tolerance);
546 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -54), -1.80143985094819845772156649015329155101490229157245556564920e16, tolerance);
547 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -64), -1.84467440737095516165772156649015328606601289230246224694513e19, tolerance);
548 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -66), -7.37869762948382064645772156649015328606199162983179574406439e19, tolerance);
549 assertClose(BoostGamma::tgamma, -Math.scalb(1.0, -33), -8.58993459257721566501667413261977598620193488449233402857632e9, tolerance);
550 assertClose(BoostGamma::tgamma, -4 / Double.MAX_VALUE, -Double.MAX_VALUE / 4, tolerance);
551 assertClose(BoostGamma::tgamma, -1 + Math.scalb(1.0, -22), -4.19430442278467170746130758391572421252211886167956799318843e6, tolerance);
552 assertClose(BoostGamma::tgamma, -1 - Math.scalb(1.0, -22), 4.19430357721600151046968956086404748206205391186399889108944e6, tolerance);
553 assertClose(BoostGamma::tgamma, -4 + Math.scalb(1.0, -20), 43690.7294216755534842491085530510391932288379640970386378756, tolerance);
554 assertClose(BoostGamma::tgamma, -4 - Math.scalb(1.0, -20), -43690.6039118698506165317137699180871126338425941292693705533, tolerance);
555 assertClose(BoostGamma::tgamma, -1 + Math.scalb(1.0, -44), -1.75921860444164227843350985473932247549232492467032584051825e13, tolerance);
556 assertClose(BoostGamma::tgamma, -1 - Math.scalb(1.0, -44), 1.75921860444155772156649016131144377791001546933519242218430e13, tolerance);
557 assertClose(BoostGamma::tgamma, -4 + Math.scalb(1.0, -44), 7.33007751850729421569517998006564998020333048893618664936994e11, tolerance);
558 assertClose(BoostGamma::tgamma, -4 - Math.scalb(1.0, -44), -7.33007751850603911763815347967171096249288790373790093559568e11, tolerance);
559
560 assertClose(BoostGamma::tgamma, 142.75, 7.8029496083318133344429227511387928576820621466e244, tolerance * 4);
561 assertClose(BoostGamma::tgamma, -Double.MIN_VALUE, Double.NEGATIVE_INFINITY, 0);
562 assertClose(BoostGamma::tgamma, Double.MIN_VALUE, Double.POSITIVE_INFINITY, 0);
563 }
564
565 @Test
566 void testLanczosGmh() {
567
568 Assertions.assertEquals(Lanczos.G - 0.5, Lanczos.GMH);
569 Assertions.assertEquals(0.5 - Lanczos.G, -Lanczos.GMH);
570 }
571
572 @ParameterizedTest
573 @EnumSource(value = TestCase.class, mode = Mode.MATCH_ANY, names = {"TGAMMAO_.*"})
574 void testTGammaOriginal(TestCase tc) {
575 assertFunction(tc);
576 }
577
578 @ParameterizedTest
579 @EnumSource(value = TestCase.class, mode = Mode.MATCH_ANY, names = {"TGAMMA_.*"})
580 void testTGamma(TestCase tc) {
581 assertFunction(tc);
582 }
583
584
585
586
587
588
589 @ParameterizedTest
590 @ValueSource(strings = {"gamma_20_150_data.csv", "gamma_factorials_data.csv"})
591 @Order(1)
592 void testTGammaWithLanczosSupport(String datafile) throws Exception {
593 final TestUtils.ErrorStatistics e1 = new TestUtils.ErrorStatistics();
594 final TestUtils.ErrorStatistics e2 = new TestUtils.ErrorStatistics();
595
596
597
598 final double tolerance = 10;
599
600 try (DataReader in = new DataReader(datafile)) {
601 while (in.next()) {
602 final double x = in.getDouble(0);
603
604
605
606 if ((int) x == x || x <= LANCZOS_THRESHOLD) {
607 continue;
608 }
609
610 final double v1 = gammaOriginal(x);
611
612
613 if (Double.isInfinite(v1)) {
614 continue;
615 }
616 final double v2 = BoostGamma.tgamma(x);
617
618 final BigDecimal expected = in.getBigDecimal(1);
619
620 TestUtils.assertEquals(expected, v1, tolerance, e1::add, () -> "Numbers 1.0 x=" + x);
621 TestUtils.assertEquals(expected, v2, tolerance, e2::add, () -> "Boost x=" + x);
622 }
623 }
624
625
626 final double maxTolerance = 6;
627 final double rmsTolerance = 2;
628 assertRms(TestError.of(datafile + " Numbers 1.0", maxTolerance, rmsTolerance), e1);
629 assertRms(TestError.of(datafile + " Boost ", maxTolerance, rmsTolerance), e2);
630 Assertions.assertTrue(e2.getRMS() < e1.getRMS() * 0.8, "Expected better precision");
631 Assertions.assertTrue(e2.getMaxAbs() < e1.getMaxAbs() * 0.7, "Expected lower max error");
632 Assertions.assertTrue(Math.abs(e2.getMean()) < Math.abs(e1.getMean()) * 0.5, "Expected better accuracy");
633 }
634
635 @ParameterizedTest
636 @CsvSource({
637
638 "0, NaN",
639 "-1, NaN",
640 "-2, NaN",
641
642 "1, 0",
643 "2, 0",
644 })
645 void testLGammaEdgeCases(double z, double p) {
646 Assertions.assertEquals(p, BoostGamma.lgamma(z));
647 }
648
649
650
651
652
653 @Test
654 void testLGammaSpotTests() {
655 final int tolerance = 1;
656 final int[] sign = {0};
657 final DoubleUnaryOperator fun = z -> BoostGamma.lgamma(z, sign);
658
659 assertClose(fun, 3.5, 1.2009736023470742248160218814507129957702389154682, tolerance);
660 Assertions.assertEquals(1, sign[0]);
661 assertClose(fun, 0.125, 2.0194183575537963453202905211670995899482809521344, tolerance);
662 Assertions.assertEquals(1, sign[0]);
663 assertClose(fun, -0.125, 2.1653002489051702517540619481440174064962195287626, tolerance);
664 Assertions.assertEquals(-1, sign[0]);
665 assertClose(fun, -3.125, 0.1543111276840418242676072830970532952413339012367, tolerance * 2);
666 Assertions.assertEquals(1, sign[0]);
667 assertClose(fun, -53249.0 / 1024, -149.43323093420259741100038126078721302600128285894, tolerance);
668 Assertions.assertEquals(-1, sign[0]);
669
670 assertClose(fun, Math.scalb(1.0, -12), Math.log(4095.42302574977164107280305038926932586783813167844235368772), tolerance);
671 Assertions.assertEquals(1, sign[0]);
672 assertClose(fun, Math.scalb(1.0, -14), Math.log(16383.4228446989052821887834066513143241996925504706815681204), tolerance);
673 Assertions.assertEquals(1, sign[0]);
674 assertClose(fun, Math.scalb(1.0, -25), Math.log(3.35544314227843645746319656372890833248893111091576093784981e7), tolerance);
675 Assertions.assertEquals(1, sign[0]);
676 assertClose(fun, Math.scalb(1.0, -27), Math.log(1.34217727422784342467508497080056807355928046680073490038257e8), tolerance);
677 Assertions.assertEquals(1, sign[0]);
678 assertClose(fun, Math.scalb(1.0, -29), Math.log(5.36870911422784336940727488260481582524683632281496706906706e8), tolerance);
679 Assertions.assertEquals(1, sign[0]);
680 assertClose(fun, Math.scalb(1.0, -35), Math.log(3.43597383674227843351272524573929605605651956475300480712955e10), tolerance);
681 Assertions.assertEquals(1, sign[0]);
682 assertClose(fun, Math.scalb(1.0, -54), Math.log(1.80143985094819834227843350984671942971248427509141008005685e16), tolerance);
683 Assertions.assertEquals(1, sign[0]);
684 assertClose(fun, Math.scalb(1.0, -64), Math.log(1.84467440737095516154227843350984671394471047428598176073616e19), tolerance);
685 Assertions.assertEquals(1, sign[0]);
686 assertClose(fun, Math.scalb(1.0, -66), Math.log(7.37869762948382064634227843350984671394068921181531525785592922800e19), tolerance);
687 Assertions.assertEquals(1, sign[0]);
688 assertClose(fun, Math.scalb(1.0, -33), Math.log(8.58993459142278433521360841138215453639282914047157884932317481977e9), tolerance);
689 Assertions.assertEquals(1, sign[0]);
690 assertClose(fun, 4 / Double.MAX_VALUE, Math.log(Double.MAX_VALUE / 4), tolerance);
691 Assertions.assertEquals(1, sign[0]);
692 assertClose(fun, -Math.scalb(1.0, -12), Math.log(4096.57745718775464971331294488248972086965434176847741450728), tolerance);
693 Assertions.assertEquals(-1, sign[0]);
694 assertClose(fun, -Math.scalb(1.0, -14), Math.log(16384.5772760354695939336148831283410381037202353359487504624), tolerance);
695 Assertions.assertEquals(-1, sign[0]);
696 assertClose(fun, -Math.scalb(1.0, -25), Math.log(3.35544325772156943776992988569766723938420508937071533029983e7), tolerance);
697 Assertions.assertEquals(-1, sign[0]);
698 assertClose(fun, -Math.scalb(1.0, -27), Math.log(1.34217728577215672270574319043497450577151370942651414968627e8), tolerance);
699 Assertions.assertEquals(-1, sign[0]);
700 assertClose(fun, -Math.scalb(1.0, -29), Math.log(5.36870912577215666743793215770406791630514293641886249382012e8), tolerance);
701 Assertions.assertEquals(-1, sign[0]);
702 assertClose(fun, -Math.scalb(1.0, -34), Math.log(1.71798691845772156649591034966100693794360502123447124928244e10), tolerance);
703 Assertions.assertEquals(-1, sign[0]);
704 assertClose(fun, -Math.scalb(1.0, -54), Math.log(1.80143985094819845772156649015329155101490229157245556564920e16), tolerance);
705 Assertions.assertEquals(-1, sign[0]);
706 assertClose(fun, -Math.scalb(1.0, -64), Math.log(1.84467440737095516165772156649015328606601289230246224694513e19), tolerance);
707 Assertions.assertEquals(-1, sign[0]);
708 assertClose(fun, -Math.scalb(1.0, -66), Math.log(7.37869762948382064645772156649015328606199162983179574406439e19), tolerance);
709 Assertions.assertEquals(-1, sign[0]);
710 assertClose(fun, -Math.scalb(1.0, -33), Math.log(8.58993459257721566501667413261977598620193488449233402857632e9), tolerance);
711 Assertions.assertEquals(-1, sign[0]);
712 assertClose(fun, -4 / Double.MAX_VALUE, Math.log(Double.MAX_VALUE / 4), tolerance);
713 Assertions.assertEquals(-1, sign[0]);
714 assertClose(fun, -1 + Math.scalb(1.0, -22), Math.log(4.19430442278467170746130758391572421252211886167956799318843e6), tolerance);
715 Assertions.assertEquals(-1, sign[0]);
716 assertClose(fun, -1 - Math.scalb(1.0, -22), Math.log(4.19430357721600151046968956086404748206205391186399889108944e6), tolerance);
717 Assertions.assertEquals(1, sign[0]);
718 assertClose(fun, -4 + Math.scalb(1.0, -20), Math.log(43690.7294216755534842491085530510391932288379640970386378756), tolerance);
719 Assertions.assertEquals(1, sign[0]);
720 assertClose(fun, -4 - Math.scalb(1.0, -20), Math.log(43690.6039118698506165317137699180871126338425941292693705533), tolerance);
721 Assertions.assertEquals(-1, sign[0]);
722
723 assertClose(fun, -1 + Math.scalb(1.0, -44), Math.log(1.75921860444164227843350985473932247549232492467032584051825e13), tolerance);
724 Assertions.assertEquals(-1, sign[0]);
725 assertClose(fun, -1 - Math.scalb(1.0, -44), Math.log(1.75921860444155772156649016131144377791001546933519242218430e13), tolerance);
726 Assertions.assertEquals(1, sign[0]);
727 assertClose(fun, -4 + Math.scalb(1.0, -44), Math.log(7.33007751850729421569517998006564998020333048893618664936994e11), tolerance);
728 Assertions.assertEquals(1, sign[0]);
729 assertClose(fun, -4 - Math.scalb(1.0, -44), Math.log(7.33007751850603911763815347967171096249288790373790093559568e11), tolerance);
730 Assertions.assertEquals(-1, sign[0]);
731
732
733
734 assertClose(fun, Math.scalb(11103367432951928.0, 32), 2.7719825960021351251696385101478518546793793286704974382373670822285114741208958e27, tolerance);
735 assertClose(fun, Math.scalb(11103367432951928.0, 62), 4.0411767712186990905102512019058204792570873633363159e36, tolerance);
736 assertClose(fun, Math.scalb(11103367432951928.0, 326), 3.9754720509185529233002820161357111676582583112671658e116, tolerance);
737
738
739
740 double value = Double.MIN_NORMAL;
741 while (value != 0) {
742 Assertions.assertTrue(Double.isFinite(fun.applyAsDouble(value)));
743 value /= 2;
744 }
745
746
747 final int[] signEmpty = {};
748 for (final double z : new double[] {3.5, 6.76, 8.12}) {
749 Assertions.assertEquals(BoostGamma.lgamma(z), BoostGamma.lgamma(z, signEmpty));
750 }
751 }
752
753 @ParameterizedTest
754 @EnumSource(value = TestCase.class, mode = Mode.MATCH_ANY, names = {"LGAMMA_.*"})
755 void testLGamma(TestCase tc) {
756 assertFunction(tc);
757 }
758
759 @ParameterizedTest
760 @CsvSource({
761
762 "-1, NaN",
763 "-2, NaN",
764
765 "0, 0",
766 "1, 0",
767 "2, 1",
768 "3, 5",
769 "4, 23",
770 "5, 119",
771 })
772 void testTGammap1m1EdgeCases(double z, double p) {
773 Assertions.assertEquals(p, BoostGamma.tgamma1pm1(z));
774 }
775
776 @ParameterizedTest
777 @EnumSource(value = TestCase.class, mode = Mode.MATCH_ANY, names = {"TGAMMAP1M1.*"})
778 void testTGammap1m1(TestCase tc) {
779 assertFunction(tc);
780 }
781
782 @ParameterizedTest
783 @CsvSource({
784 "0, 1, -1",
785 "0, 0, 0",
786 "0, -1, Infinity",
787 "2, -2, -0.75",
788 "2, 1024, Infinity",
789 "2, -1075, -1",
790 "NaN, 1, NaN",
791 "1, NaN, NaN",
792
793 "-2, 2, 3",
794
795 "-2, 2.1, NaN",
796 })
797 void testPowm1EdgeCases(double x, double y, double expected) {
798 Assertions.assertEquals(expected, BoostMath.powm1(x, y));
799 }
800
801 @ParameterizedTest
802 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"POWM1.*"})
803 void testPowm1(BiTestCase tc) {
804 assertFunction(tc);
805 }
806
807
808
809
810
811
812 @ParameterizedTest
813 @ValueSource(doubles = {-1.1, -1, -0.9, 0, 1, 1.5, 2, 3})
814 void testLog1pmxStandard(double x) {
815 Assertions.assertEquals(Math.log1p(x) - x, SpecialMath.log1pmx(x));
816 }
817
818
819
820
821
822
823 @ParameterizedTest
824 @EnumSource(value = TestCase.class, mode = Mode.MATCH_ANY, names = {"LOG1PMX.*"})
825 void testLog1pmx(TestCase tc) {
826 assertFunction(tc);
827 }
828
829 @ParameterizedTest
830 @CsvSource({
831
832 "NaN, 1, NaN",
833 "0, 1, NaN",
834 "-1, 1, NaN",
835
836 "1, NaN, NaN",
837 "1, -1, NaN",
838 })
839 void testIGammaEdgeCases(double a, double z, double expected) {
840
841 Assertions.assertEquals(expected, BoostGamma.tgamma(a, z), "tgamma");
842 Assertions.assertEquals(expected, BoostGamma.tgammaLower(a, z), "tgammaLower");
843 Assertions.assertEquals(expected, BoostGamma.gammaP(a, z), "gammaP");
844 Assertions.assertEquals(expected, BoostGamma.gammaQ(a, z), "gammaQ");
845 Assertions.assertEquals(expected, BoostGamma.gammaPDerivative(a, z), "gammaPDerivative");
846 }
847
848 @ParameterizedTest
849 @CsvSource({
850
851 "2, 0, 0",
852 "1, 0, 1",
853 "0.5, 0, Infinity",
854 })
855 void testGammaPDerivativeEdgeCases(double a, double z, double expected) {
856 Assertions.assertEquals(expected, BoostGamma.gammaPDerivative(a, z));
857 }
858
859
860
861
862
863 @Test
864 void testIGammaSpotTests() {
865 int tolerance = 10;
866 assertClose(BoostGamma::tgamma, 5, 1, 23.912163676143750903709045060494956383977723517065, tolerance);
867 assertClose(BoostGamma::tgamma, 5, 5, 10.571838841565097874621959975919877646444998907920, tolerance);
868 assertClose(BoostGamma::tgamma, 5, 10, 0.70206451384706574414638719662835463671916532623256, tolerance);
869 assertClose(BoostGamma::tgamma, 5, 100, 3.8734332808745531496973774140085644548465762343719e-36, tolerance);
870 assertClose(BoostGamma::tgamma, 0.5, 0.5, 0.56241823159440712427949495730204306902676756479651, tolerance);
871 assertClose(BoostGamma::tgamma, 0.5, 9.0 / 10, 0.31853210360412109873859360390443790076576777747449, tolerance * 10);
872 assertClose(BoostGamma::tgamma, 0.5, 5, 0.0027746032604128093194908357272603294120210079791437, tolerance);
873 assertClose(BoostGamma::tgamma, 0.5, 100, 3.7017478604082789202535664481339075721362102520338e-45, tolerance);
874
875 assertClose(BoostGamma::tgammaLower, 5, 1, 0.087836323856249096290954939505043616022276482935091, tolerance);
876 assertClose(BoostGamma::tgammaLower, 5, 5, 13.428161158434902125378040024080122353555001092080, tolerance);
877 assertClose(BoostGamma::tgammaLower, 5, 10, 23.297935486152934255853612803371645363280834673767, tolerance);
878 assertClose(BoostGamma::tgammaLower, 5, 100, 23.999999999999999999999999999999999996126566719125, tolerance);
879
880 assertClose(BoostGamma::gammaQ, 5, 1, 0.99634015317265628765454354418728984933240514654437, tolerance);
881 assertClose(BoostGamma::gammaQ, 5, 5, 0.44049328506521241144258166566332823526854162116334, tolerance);
882 assertClose(BoostGamma::gammaQ, 5, 10, 0.029252688076961072672766133192848109863298555259690, tolerance);
883 assertClose(BoostGamma::gammaQ, 5, 100, 1.6139305336977304790405739225035685228527400976549e-37, tolerance);
884 assertClose(BoostGamma::gammaQ, 1.5, 2, 0.26146412994911062220282207597592120190281060919079, tolerance);
885 assertClose(BoostGamma::gammaQ, 20.5, 22, 0.34575332043467326814971590879658406632570278929072, tolerance);
886
887 assertClose(BoostGamma::gammaP, 5, 1, 0.0036598468273437123454564558127101506675948534556288, tolerance);
888 assertClose(BoostGamma::gammaP, 5, 5, 0.55950671493478758855741833433667176473145837883666, tolerance);
889 assertClose(BoostGamma::gammaP, 5, 10, 0.97074731192303892732723386680715189013670144474031, tolerance);
890 assertClose(BoostGamma::gammaP, 5, 100, 0.9999999999999999999999999999999999998386069466302, tolerance);
891 assertClose(BoostGamma::gammaP, 1.5, 2, 0.73853587005088937779717792402407879809718939080921, tolerance);
892 assertClose(BoostGamma::gammaP, 20.5, 22, 0.65424667956532673185028409120341593367429721070928, tolerance);
893
894
895 tolerance = 50;
896 assertClose(BoostGamma::gammaPDerivative, 20.5, 22,
897 Math.exp(-22) * Math.pow(22, 19.5) / BoostGamma.tgamma(20.5), tolerance);
898
899
900 assertClose(BoostGamma::tgamma, 20, Math.scalb(1.0, -40), 1.21645100408832000000e17, tolerance);
901 assertClose(BoostGamma::tgammaLower, 20, Math.scalb(1.0, -40), 7.498484069471659696438206828760307317022658816757448882e-243, tolerance);
902 assertClose(BoostGamma::gammaP, 20, Math.scalb(1.0, -40), 6.164230243774976473534975936127139110276824507876192062e-260, tolerance);
903
904 assertClose(BoostGamma::tgamma, 30, Math.scalb(1.0, -30), 8.841761993739701954543616000000e30, tolerance);
905 assertClose(BoostGamma::tgammaLower, 30, Math.scalb(1.0, -30), 3.943507283668378474979245322638092813837393749566146974e-273, tolerance);
906 assertClose(BoostGamma::gammaP, 30, Math.scalb(1.0, -30), 4.460092102072560946444018923090222645613009128135650652e-304, tolerance);
907 assertClose(BoostGamma::gammaPDerivative, 2, Math.scalb(1.0, -575), 8.08634922390438981326119906687585206568664784377654648227177e-174, tolerance);
908
909 assertEquals(BoostGamma::tgamma, 176, 100, Double.POSITIVE_INFINITY);
910 assertEquals(BoostGamma::tgamma, 530, 2000, Double.POSITIVE_INFINITY);
911 assertEquals(BoostGamma::tgamma, 740, 2500, Double.POSITIVE_INFINITY);
912 assertEquals(BoostGamma::tgamma, 530.5, 2000, Double.POSITIVE_INFINITY);
913 assertEquals(BoostGamma::tgamma, 740.5, 2500, Double.POSITIVE_INFINITY);
914 assertEquals(BoostGamma::tgammaLower, 10000.0f, 10000.0f / 4, Double.POSITIVE_INFINITY);
915 assertClose(BoostGamma::tgamma, 170, 165, 2.737338337642022829223832094019477918166996032112404370e304, tolerance);
916 assertClose(BoostGamma::tgammaLower, 170, 165, 1.531729671362682445715419794880088619901822603944331733e304, tolerance);
917
918 assertClose(BoostGamma::tgamma, 170, 170, 2.090991698081449410761040647015858316167077909285580375e304, 16 * tolerance);
919 assertClose(BoostGamma::tgammaLower, 170, 170, 2.178076310923255864178211241883708221901740726771155728e304, 16 * tolerance);
920 assertClose(BoostGamma::tgamma, 170, 190, 2.8359275512790301602903689596273175148895758522893941392e303, 10 * tolerance);
921 assertClose(BoostGamma::tgammaLower, 170, 190, 3.985475253876802258910214992936834786579861050827796689e304, 10 * tolerance);
922
923 assertClose(BoostGamma::tgamma, 170, 1000, 6.1067635957780723069200425769800190368662985052038980542e72, 16 * tolerance);
924
925 assertClose(BoostGamma::tgammaLower, 185, 1, 0.001999286058955490074702037576083582139834300307968257924836, tolerance);
926 assertClose(BoostGamma::tgamma, 185, 1500, 1.037189524841404054867100938934493979112615962865368623e-67, tolerance * 10);
927
928 assertClose(BoostGamma::tgamma, 36, Math.scalb(1.0, -26), 1.03331479663861449296666513375232000000e40, tolerance * 10);
929 assertClose(BoostGamma::tgamma, 50.5, Math.scalb(1.0, -17), 4.2904629123519598109157551960589377e63, tolerance * 10);
930 assertClose(BoostGamma::tgamma, 164.5, 0.125, 2.5649307433687542701168405519538910e292, tolerance * 10);
931
932
933
934 final double maxVal = Double.MAX_VALUE;
935 final double largeVal = maxVal * 0.99f;
936 assertEquals(BoostGamma::tgamma, 22.25, maxVal, 0);
937 assertEquals(BoostGamma::tgamma, 22.25, largeVal, 0);
938 assertEquals(BoostGamma::tgammaLower, 22.25, maxVal, BoostGamma.tgamma(22.25));
939 assertEquals(BoostGamma::tgammaLower, 22.25, largeVal, BoostGamma.tgamma(22.25));
940 assertEquals(BoostGamma::gammaQ, 22.25, maxVal, 0);
941 assertEquals(BoostGamma::gammaQ, 22.25, largeVal, 0);
942 assertEquals(BoostGamma::gammaP, 22.25, maxVal, 1);
943 assertEquals(BoostGamma::gammaP, 22.25, largeVal, 1);
944 assertEquals(BoostGamma::tgamma, 22.25, Double.POSITIVE_INFINITY, 0);
945 assertEquals(BoostGamma::tgammaLower, 22.25, Double.POSITIVE_INFINITY, BoostGamma.tgamma(22.25));
946 assertEquals(BoostGamma::gammaQ, 22.25, Double.POSITIVE_INFINITY, 0);
947 assertEquals(BoostGamma::gammaP, 22.25, Double.POSITIVE_INFINITY, 1);
948
949
950
951
952 assertEquals(BoostGamma::gammaQ, 1770, 1e-12, 1);
953 assertEquals(BoostGamma::gammaP, 1770, 1e-12, 0);
954 }
955
956 @ParameterizedTest
957 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"IGAMMA_U.*"})
958 void testIGammaUpper(BiTestCase tc) {
959 assertFunction(tc);
960 }
961
962 @ParameterizedTest
963 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"IGAMMA_L.*"})
964 void testIGammaLower(BiTestCase tc) {
965 assertFunction(tc);
966 }
967
968 @ParameterizedTest
969 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"IGAMMA_Q.*"})
970 void testIGammaQ(BiTestCase tc) {
971 assertFunction(tc);
972 }
973
974 @ParameterizedTest
975 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"IGAMMA_P.*"})
976 void testIGammaP(BiTestCase tc) {
977 assertFunction(tc);
978 }
979
980 @ParameterizedTest
981 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"GAMMA_P_DERIV.*"})
982 void testGammaPDerivative(BiTestCase tc) {
983 assertFunction(tc);
984 }
985
986 @ParameterizedTest
987 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"LOG_GAMMA_P_DERIV.*"})
988 void testLogGammaPDerivative(BiTestCase tc) {
989 assertFunction(tc);
990 }
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003 @ParameterizedTest
1004 @CsvSource(value = {
1005
1006
1007
1008
1009 "5.0,2.5,21.38827245393963,0.8911780189141513,2.6117275460603704,0.10882198108584876",
1010
1011 "19.24400520324707,21.168405532836914,4.0308280447358675E15,0.3084240508178698,9.038282597080282E15,0.6915759491821302",
1012
1013 "664.0791015625,1328.158203125,Infinity,4.90100553385586E-91,Infinity,1.0",
1014
1015 "0.9759566783905029,1.0735523700714111,0.33659577343416824,0.33179703084688433,0.6778671124302277,0.6682029691531157",
1016
1017 "0.4912221431732178,0.9824442863464355,0.2840949896471149,0.1575143024618326,1.519518937513272,0.8424856975381674",
1018 })
1019 void testIGammaPolicy(double a, double x, double upper, double q, double lower, double p) {
1020
1021 final Policy pol1 = new Policy(0x1.0p-52, 1);
1022 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.tgamma(a, x, pol1), "upper");
1023 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.tgammaLower(a, x, pol1), "lower");
1024 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.gammaP(a, x, pol1), "p");
1025 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.gammaQ(a, x, pol1), "q");
1026
1027
1028 final Policy pol2 = new Policy(1e-3, Integer.MAX_VALUE);
1029
1030
1031 if (Double.isFinite(upper)) {
1032 final double u1 = BoostGamma.tgamma(a, x);
1033 final double u2 = BoostGamma.tgamma(a, x, pol2);
1034 assertCloser("upper", upper, u1, u2);
1035 }
1036 if (Double.isFinite(lower)) {
1037 final double l1 = BoostGamma.tgammaLower(a, x);
1038 final double l2 = BoostGamma.tgammaLower(a, x, pol2);
1039 assertCloser("lower", lower, l1, l2);
1040 }
1041
1042
1043 if ((int) p != p) {
1044 final double p1 = BoostGamma.gammaP(a, x);
1045 final double p2 = BoostGamma.gammaP(a, x, pol2);
1046 assertCloser("p", p, p1, p2);
1047 }
1048 if ((int) q != q) {
1049 final double q1 = BoostGamma.gammaQ(a, x);
1050 final double q2 = BoostGamma.gammaQ(a, x, pol2);
1051 assertCloser("q", q, q1, q2);
1052 }
1053 }
1054
1055 @Test
1056 void testIGammaPolicy1() {
1057
1058 final double a = 230.1575469970703125;
1059 final double x = 23015.75390625;
1060
1061 final double upper = 0;
1062 final double u1 = BoostGamma.tgamma(a, x);
1063 Assertions.assertEquals(upper, u1);
1064 final Policy pol1 = new Policy(0x1.0p-52, 1);
1065 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.tgamma(a, x, pol1), "upper");
1066
1067 }
1068
1069 @Test
1070 void testIGammaPolicy2() {
1071
1072 final double a = 5823.5341796875;
1073 final double x = 1.0;
1074 final double lower = 0.6318201301512319242829e-4;
1075 final double l1 = BoostGamma.tgammaLower(a, x);
1076 Assertions.assertEquals(lower, l1, lower * 1e-10);
1077 final Policy pol1 = new Policy(0x1.0p-52, 1);
1078 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.tgammaLower(a, x, pol1), "lower");
1079 final Policy pol2 = new Policy(1e-3, Integer.MAX_VALUE);
1080 assertCloser("lower", lower, l1, BoostGamma.tgammaLower(a, x, pol2));
1081 }
1082
1083 @Test
1084 void testIGammaPolicy3() {
1085
1086
1087
1088
1089
1090
1091 final double a = 53731.765625;
1092 final double x = 26865.8828125;
1093 final double lower = Double.POSITIVE_INFINITY;
1094 final double l1 = BoostGamma.tgammaLower(a, x);
1095 Assertions.assertEquals(lower, l1);
1096 final Policy pol1 = new Policy(0x1.0p-52, 1);
1097 Assertions.assertThrows(ArithmeticException.class, () -> BoostGamma.tgammaLower(a, x, pol1), "lower");
1098 }
1099
1100
1101
1102
1103 private static void assertCloser(String msg, double expected, double x, double y) {
1104 final double dx = Math.abs(expected - x);
1105 final double dy = Math.abs(expected - y);
1106 Assertions.assertTrue(dx < dy,
1107 () -> String.format("%s %s : %s (%s) : %s (%s)", msg, expected, x, dx, y, dy));
1108 }
1109
1110
1111
1112
1113
1114 @ParameterizedTest
1115 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"IGAMMA_LARGE_X.*"})
1116 void testIGammaLargeX(BiTestCase tc) {
1117 assertFunction(tc);
1118 }
1119
1120 @Test
1121 void testIGammaAsymptoticApproximationFailsOnVeryLargeX() {
1122 final double a = 1e30;
1123 final double x = a + Math.ulp(a);
1124
1125 Assertions.assertTrue(a < x);
1126
1127 Assertions.assertEquals(a, a - 1);
1128
1129
1130
1131 final Policy pol = new Policy(Math.ulp(1.0), 10000000);
1132 Assertions.assertThrows(ArithmeticException.class,
1133 () -> BoostGamma.incompleteTgammaLargeX(a, x, pol));
1134 }
1135
1136
1137
1138
1139
1140
1141
1142
1143 @ParameterizedTest
1144 @ValueSource(strings = {"igamma_int_data.csv", "igamma_med_data.csv", "igamma_big_data.csv"})
1145 @Order(1)
1146 void testGammaQLargeXOriginal(String datafile) throws Exception {
1147 assertIgammaLargeX("Boost", datafile, true, USE_ASYM_APPROX, USE_ASYM_APPROX, false);
1148 }
1149
1150
1151
1152
1153
1154 @ParameterizedTest
1155 @ValueSource(strings = {"igamma_int_data.csv", "igamma_med_data.csv", "igamma_big_data.csv"})
1156 @Order(1)
1157 void testGammaPLargeXOriginal(String datafile) throws Exception {
1158 assertIgammaLargeX("Boost", datafile, false, USE_ASYM_APPROX, USE_ASYM_APPROX, false);
1159 }
1160
1161
1162
1163
1164
1165 @ParameterizedTest
1166 @ValueSource(strings = {"igamma_int_data.csv", "igamma_med_data.csv", "igamma_big_data.csv"})
1167 @Order(1)
1168 void testGammaQLargeX(String datafile) throws Exception {
1169 assertIgammaLargeX("Commons", datafile, true, getLargeXTarget(), getUseAsymApprox(), true);
1170 }
1171
1172
1173
1174
1175
1176 @ParameterizedTest
1177 @ValueSource(strings = {"igamma_int_data.csv", "igamma_med_data.csv", "igamma_big_data.csv"})
1178 @Order(1)
1179 void testGammaPLargeX(String datafile) throws Exception {
1180 assertIgammaLargeX("Commons", datafile, false, getLargeXTarget(), getUseAsymApprox(), true);
1181 }
1182
1183
1184
1185
1186 private static DoubleDoubleBiPredicate getLargeXTarget() {
1187
1188
1189 return USE_ASYM_APPROX;
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200 }
1201
1202
1203
1204
1205 private static DoubleDoubleBiPredicate getUseAsymApprox() {
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250 return (a, x) -> (x > 1000) && (a < x * 0.75);
1251 }
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281 private static void assertIgammaLargeX(String name, String datafile, boolean invert,
1282 DoubleDoubleBiPredicate targetData,
1283 DoubleDoubleBiPredicate useAsymp,
1284 boolean expectImprovement) throws Exception {
1285 final TestUtils.ErrorStatistics e1 = new TestUtils.ErrorStatistics();
1286 final TestUtils.ErrorStatistics e2 = new TestUtils.ErrorStatistics();
1287
1288 final Policy pol = Policy.getDefault();
1289 final DoubleBinaryOperator without = (a, x) -> gammaIncompleteImp(a, x, invert, pol, NO_ASYM_APPROX);
1290 final DoubleBinaryOperator with = (a, x) -> gammaIncompleteImp(a, x, invert, pol, useAsymp);
1291 final int expectedField = invert ? 3 : 5;
1292
1293
1294 int count = 0;
1295
1296 final String functionName = datafile + " " + name + (invert ? " Q" : " P");
1297
1298
1299
1300 final double tolerance = 1000;
1301
1302 try (DataReader in = new DataReader(datafile)) {
1303 while (in.next()) {
1304 final double a = in.getDouble(0);
1305 final double x = in.getDouble(1);
1306
1307
1308 if (targetData.test(a, x)) {
1309 final BigDecimal expected = in.getBigDecimal(expectedField);
1310
1311
1312
1313 final double value = expected.doubleValue();
1314 if ((int) value != value) {
1315 final double v1 = without.applyAsDouble(a, x);
1316 final double v2 = with.applyAsDouble(a, x);
1317
1318 if (useAsymp.test(a, x)) {
1319 count++;
1320 }
1321
1322 TestUtils.assertEquals(expected, v1, tolerance, e1::add,
1323 () -> functionName + " " + a + ", x=" + x);
1324 TestUtils.assertEquals(expected, v2, tolerance, e2::add,
1325 () -> functionName + " asymp " + a + ", x=" + x);
1326 }
1327 }
1328 }
1329 }
1330
1331
1332
1333 final double maxTolerance = 600;
1334 final double rmsTolerance = 600;
1335 if (e1.size() != 0) {
1336 assertRms(TestError.of(functionName + " ", maxTolerance, rmsTolerance), e1);
1337 assertRms(TestError.of(functionName + " asymp " +
1338 String.format("%4d", count), maxTolerance, rmsTolerance), e2);
1339 if (expectImprovement) {
1340
1341
1342 Assertions.assertTrue(e2.getRMS() <= e1.getRMS());
1343 } else {
1344
1345 Assertions.assertTrue(e2.getRMS() > e1.getRMS());
1346 }
1347 }
1348 }
1349
1350
1351
1352
1353
1354 @ParameterizedTest
1355 @CsvSource({
1356
1357
1358
1359
1360
1361 "-1074,-1074,3.67517082493672000135e-321",
1362 "-1074,-1073,3.67174622284245611609e-321",
1363 "-1074,-1072,3.66832162074819223109e-321",
1364 "-1074,-1000,3.4217502699611925034e-321",
1365 "-1074,-100,3.3960838512369590694e-322",
1366 "-1074,-10,3.13990203220802065461e-323",
1367 "-1074,-5,1.44243837956526236902e-323",
1368 "-1030,-1074,6.46542888972966038541e-308",
1369 "-1030,-1073,6.45940426601262169248e-308",
1370 "-1030,-1072,6.45337964229558300003e-308",
1371 "-1030,-1000,6.01960673466879712923e-308",
1372 "-1030,-100,5.97445389333973743599e-309",
1373 "-1030,-10,5.52377407118433787103e-310",
1374 "-1030,-5,2.53756443309180378013e-310",
1375 })
1376 void testGammaQTinyA(int ba, int bx, double q) {
1377 final double a = Math.scalb(1.0, ba);
1378 final double x = Math.scalb(1.0, bx);
1379 final double actualQ = BoostGamma.gammaQ(a, x);
1380
1381
1382
1383 Assertions.assertNotEquals(0.0, actualQ);
1384
1385
1386 if (q < 1e-320) {
1387
1388 TestUtils.assertEquals(q, actualQ, 1);
1389 } else {
1390
1391
1392 final double relError = 1e-13;
1393 Assertions.assertEquals(q, actualQ, q * relError);
1394 }
1395 }
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410 static double tgammaOriginal(double z) {
1411 double result = 1;
1412
1413 if (z <= 0) {
1414 if (Math.rint(z) == z) {
1415
1416 return Double.NaN;
1417 }
1418 if (z <= -20) {
1419 result = BoostGamma.tgamma(-z) * BoostGamma.sinpx(z);
1420
1421 return -Math.PI / result;
1422 }
1423
1424
1425
1426
1427 while (z < 0) {
1428 result /= z;
1429 z += 1;
1430 }
1431 }
1432
1433
1434
1435
1436
1437 if ((Math.rint(z) == z) && (z <= MAX_FACTORIAL + 1)) {
1438
1439 result *= FACTORIAL[(int) z - 1];
1440 } else if (z < ROOT_EPSILON) {
1441 result *= 1 / z - EULER;
1442 } else {
1443 result *= Lanczos.lanczosSum(z);
1444 final double zgh = z + Lanczos.G - 0.5;
1445 final double lzgh = Math.log(zgh);
1446 if (z * lzgh > LOG_MAX_VALUE) {
1447
1448 if (lzgh * z / 2 > LOG_MAX_VALUE) {
1449 return Double.POSITIVE_INFINITY;
1450 }
1451 final double hp = Math.pow(zgh, (z / 2) - 0.25);
1452 result *= hp / Math.exp(zgh);
1453
1454
1455 result *= hp;
1456 } else {
1457 result *= Math.pow(zgh, z - 0.5) / Math.exp(zgh);
1458 }
1459 }
1460 return result;
1461 }
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476 static double gammaOriginal(double x) {
1477 Assertions.assertTrue(x > LANCZOS_THRESHOLD, "Unsupported x: " + x);
1478
1479 final double y = x + LanczosApproximation.g() + 0.5;
1480
1481 return 2.506628274631000502 / x *
1482 Math.pow(y, x + 0.5) *
1483 Math.exp(-y) * LanczosApproximation.value(x);
1484 }
1485
1486
1487
1488
1489
1490
1491
1492
1493 private static double igammaQLargeXNoAsym(double a, double x) {
1494 return gammaIncompleteImp(a, x, true, Policy.getDefault(), NO_ASYM_APPROX);
1495 }
1496
1497
1498
1499
1500
1501
1502
1503
1504 private static double igammaPLargeXNoAsym(double a, double x) {
1505 return gammaIncompleteImp(a, x, false, Policy.getDefault(), NO_ASYM_APPROX);
1506 }
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516 private static double igammaQLargeXWithAsym(double a, double x) {
1517
1518 return gammaIncompleteImp(a, x, true, Policy.getDefault(), ASYM_APPROX);
1519 }
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529 private static double igammaPLargeXWithAsym(double a, double x) {
1530
1531 return gammaIncompleteImp(a, x, false, Policy.getDefault(), ASYM_APPROX);
1532 }
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547 private static double gammaIncompleteImp(double a, double x,
1548 boolean invert, Policy pol, DoubleDoubleBiPredicate useAsymApprox) {
1549 if (Double.isNaN(a) || Double.isNaN(x) || a <= 0 || x < 0) {
1550 return Double.NaN;
1551 }
1552
1553
1554
1555
1556
1557 Assertions.assertFalse(isIntOrHalfInt(a, x), () -> "Invalid a : " + a);
1558 Assertions.assertFalse(x < 1.1, () -> "Invalid x : " + x);
1559
1560 double result = 0;
1561 int evalMethod;
1562
1563
1564
1565
1566 if (useAsymApprox.test(a, x)) {
1567
1568
1569
1570
1571
1572
1573
1574
1575 invert = !invert;
1576 evalMethod = 7;
1577 } else {
1578
1579
1580
1581
1582
1583 boolean useTemme = false;
1584 if (a > 20) {
1585 final double sigma = Math.abs((x - a) / a);
1586 if (a > 200) {
1587
1588
1589
1590
1591
1592
1593
1594
1595 if (20 / a > sigma * sigma) {
1596 useTemme = true;
1597 }
1598 } else {
1599
1600
1601
1602 if (sigma < 0.4) {
1603 useTemme = true;
1604 }
1605 }
1606 }
1607 if (useTemme) {
1608 evalMethod = 5;
1609 } else {
1610
1611
1612
1613
1614
1615
1616
1617
1618 if (x - (1 / (3 * x)) < a) {
1619 evalMethod = 2;
1620 } else {
1621 evalMethod = 4;
1622 invert = !invert;
1623 }
1624 }
1625 }
1626
1627 switch (evalMethod) {
1628 case 2:
1629
1630 result = BoostGamma.regularisedGammaPrefix(a, x);
1631 if (result != 0) {
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645 double initValue = 0;
1646 boolean optimisedInvert = false;
1647 if (invert) {
1648
1649 initValue = 1;
1650 initValue /= result;
1651 initValue *= -a;
1652 optimisedInvert = true;
1653 }
1654 result *= BoostGamma.lowerGammaSeries(a, x, initValue, pol) / a;
1655 if (optimisedInvert) {
1656 invert = false;
1657 result = -result;
1658 }
1659 }
1660 break;
1661 case 4:
1662
1663 result = BoostGamma.regularisedGammaPrefix(a, x);
1664 if (result != 0) {
1665 result *= BoostGamma.upperGammaFraction(a, x, pol);
1666 }
1667 break;
1668 case 5:
1669
1670 result = BoostGamma.igammaTemmeLarge(a, x);
1671 if (x >= a) {
1672 invert = !invert;
1673 }
1674 break;
1675 case 7:
1676
1677
1678 result = BoostGamma.regularisedGammaPrefix(a, x);
1679 result /= x;
1680 if (result != 0) {
1681 result *= BoostGamma.incompleteTgammaLargeX(a, x, pol);
1682 }
1683 break;
1684 default:
1685 Assertions.fail(String.format("Unknown evaluation method: %s %s", a, x));
1686 }
1687
1688 if (result > 1) {
1689 result = 1;
1690 }
1691 if (invert) {
1692 result = 1 - result;
1693 }
1694
1695 return result;
1696 }
1697
1698
1699
1700
1701
1702
1703
1704
1705 private static boolean isIntOrHalfInt(double a, double x) {
1706 boolean isInt;
1707 boolean isHalfInt;
1708 final boolean isSmallA = (a < 30) && (a <= x + 1) && (-x < LOG_MIN_VALUE);
1709 if (isSmallA) {
1710 final double fa = Math.floor(a);
1711 isInt = fa == a;
1712 isHalfInt = !isInt && (Math.abs(fa - a) == 0.5f);
1713 } else {
1714 isInt = isHalfInt = false;
1715 }
1716 return isInt || isHalfInt;
1717 }
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728 private static double incompleteTgammaLargeX(double a, double x) {
1729 return BoostGamma.incompleteTgammaLargeX(a, x, Policy.getDefault());
1730 }
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745 private static double logGammaPDerivative1(double a, double x) {
1746 if (a == 1) {
1747
1748 return -x;
1749 }
1750
1751 return a * Math.log(x) - x - BoostGamma.lgamma(a) - Math.log(x);
1752
1753
1754
1755
1756
1757
1758
1759 }
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775 private static double logGammaPDerivative2(double a, double x) {
1776
1777
1778
1779 if (Double.isNaN(a) || Double.isNaN(x) || a <= 0 || x < 0) {
1780 return Double.NaN;
1781 }
1782
1783
1784
1785 if (x == 0) {
1786 return (a > 1) ? Double.NEGATIVE_INFINITY : (a == 1) ? 0 : Double.POSITIVE_INFINITY;
1787 }
1788 if (a == 1) {
1789 return -x;
1790 }
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803 final double v = BoostGamma.gammaPDerivative(a, x);
1804 return Double.isFinite(v) && v != 0 ?
1805 Math.log(v) :
1806 logGammaPDerivative1(a, x);
1807 }
1808
1809 @ParameterizedTest
1810 @CsvSource({
1811 "NaN, 1, NaN",
1812 "1, NaN, NaN",
1813
1814 "Infinity, 0, 1",
1815 "Infinity, 1, 0",
1816 "Infinity, -1, Infinity",
1817
1818
1819 "-1, 0.5, NaN",
1820 "-1.5, 0.5, NaN",
1821 "0.5, -1.5, NaN",
1822 })
1823 void testGammaDeltaRatioEdgeCases(double a, double delta, double expected) {
1824 Assertions.assertEquals(expected, BoostGamma.tgammaDeltaRatio(a, delta));
1825 }
1826
1827 @ParameterizedTest
1828 @CsvSource({
1829 "0, 1, NaN",
1830 "-1, 1, NaN",
1831 "Infinity, 1, NaN",
1832 "NaN, 1, NaN",
1833 "1, 0, NaN",
1834 "1, -1, NaN",
1835 "1, Infinity, NaN",
1836 "1, NaN, NaN",
1837
1838 "0.5, 500, 0",
1839
1840 "500, 0.5, Infinity",
1841 })
1842 void testGammaRatioEdgeCases(double a, double b, double expected) {
1843 Assertions.assertEquals(expected, BoostGamma.tgammaRatio(a, b));
1844 }
1845
1846
1847
1848
1849
1850 @Test
1851 void testGammaRatioSpotTests() {
1852 final int tol = 20;
1853 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -500), 180.25, 8.0113754557649679470816892372669519037339812035512e-178, 3 * tol);
1854 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -525), 192.25, 1.5966560279353205461166489184101261541784867035063e-197, 3 * tol);
1855 assertClose(BoostGamma::tgammaRatio, 182.25, Math.scalb(1.0, -500), 4.077990437521002194346763299159975185747917450788e+181, 3 * tol);
1856 assertClose(BoostGamma::tgammaRatio, 193.25, Math.scalb(1.0, -525), 1.2040790040958522422697601672703926839178050326148e+199, 3 * tol);
1857 assertClose(BoostGamma::tgammaRatio, 193.25, 194.75, 0.00037151765099653237632823607820104961270831942138159, 3 * tol);
1858
1859
1860
1861 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1020), 100, 1.20390418056093374068585549133304106854441830616070800417660e151, tol);
1862 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1020), 150, 2.94980580122226729924781231239336413648584663386992050529324e46, tol);
1863 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1020), 180, 1.00669209319561468911303652019446665496398881230516805140750e-20, tol);
1864 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1020), 220, 1.08230263539550701700187215488533416834407799907721731317227e-112, tol);
1865 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1020), 260, 7.62689807594728483940172477902929825624752380292252137809206e-208, tol);
1866 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1020), 290, 5.40206998243175672775582485422795773284966068149812072521290e-281, tol);
1867 assertClose(BoostGamma::tgammaDeltaRatio, Math.scalb(1.0, -1020), Math.scalb(1.0, -1020), 2, tol);
1868
1869 assertClose(BoostGamma::tgammaRatio, Math.scalb(1.0, -1074), 200, 5.13282785052571536804189023927976812551830809667482691717029e-50, tol * 50);
1870 assertClose(BoostGamma::tgammaRatio, 200, Math.scalb(1.0, -1074), 1.94824379293682687942882944294875087145333536754699303593931e49, tol * 10);
1871 assertClose(BoostGamma::tgammaDeltaRatio, Math.scalb(1.0, -1074), 200, 5.13282785052571536804189023927976812551830809667482691717029e-50, tol * 10);
1872 assertClose(BoostGamma::tgammaDeltaRatio, 200, Math.scalb(1.0, -1074), 1, tol);
1873
1874
1875 for (final double z : new double[] {-0.5, -15.5, -25.5}) {
1876 for (final double delta : new double[] {0.25, -0.25}) {
1877 Assertions.assertEquals(BoostGamma.tgamma(z) / BoostGamma.tgamma(z + delta), BoostGamma.tgammaDeltaRatio(z, delta));
1878 }
1879 }
1880 }
1881
1882 @ParameterizedTest
1883 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"TGAMMA_DELTA_RATIO.*"})
1884 void testGammaDeltaRatio(BiTestCase tc) {
1885 assertFunction(tc);
1886 }
1887
1888 @ParameterizedTest
1889 @EnumSource(value = BiTestCase.class, mode = Mode.MATCH_ANY, names = {"TGAMMA_RATIO.*"})
1890 void testGammaRatio(BiTestCase tc) {
1891 assertFunction(tc);
1892 }
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903 private static double tgammaDeltaRatioNegative(double z, double delta) {
1904 return BoostGamma.tgammaDeltaRatio(z, -delta);
1905 }
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915 private static void assertClose(DoubleUnaryOperator fun, double x, double expected, int tolerance) {
1916 final double actual = fun.applyAsDouble(x);
1917 TestUtils.assertEquals(expected, actual, tolerance, null, () -> Double.toString(x));
1918 }
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929 private static void assertClose(DoubleBinaryOperator fun, double x, double y, double expected, int tolerance) {
1930 final double actual = fun.applyAsDouble(x, y);
1931 TestUtils.assertEquals(expected, actual, tolerance, null, () -> x + ", " + y);
1932 }
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942 private static void assertEquals(DoubleBinaryOperator fun, double x, double y, double expected) {
1943 final double actual = fun.applyAsDouble(x, y);
1944 Assertions.assertEquals(expected, actual, () -> x + ", " + y);
1945 }
1946
1947
1948
1949
1950
1951
1952 private static void assertFunction(TestCase tc) {
1953 final TestUtils.ErrorStatistics stats = new TestUtils.ErrorStatistics();
1954 try (DataReader in = new DataReader(tc.getFilename())) {
1955 while (in.next()) {
1956 try {
1957 final double x = in.getDouble(0);
1958 final BigDecimal expected = in.getBigDecimal(tc.getExpectedField());
1959 final double actual = tc.getFunction().applyAsDouble(x);
1960 TestUtils.assertEquals(expected, actual, tc.getTolerance(), stats::add,
1961 () -> tc + " x=" + x);
1962 } catch (final NumberFormatException ex) {
1963 Assertions.fail("Failed to load data: " + Arrays.toString(in.getFields()), ex);
1964 }
1965 }
1966 } catch (final IOException ex) {
1967 Assertions.fail("Failed to load data: " + tc.getFilename(), ex);
1968 }
1969
1970 assertRms(tc, stats);
1971 }
1972
1973
1974
1975
1976
1977
1978 private static void assertFunction(BiTestCase tc) {
1979 final TestUtils.ErrorStatistics stats = new TestUtils.ErrorStatistics();
1980 try (DataReader in = new DataReader(tc.getFilename())) {
1981 while (in.next()) {
1982 try {
1983 final double x = in.getDouble(0);
1984 final double y = in.getDouble(1);
1985 final BigDecimal expected = in.getBigDecimal(tc.getExpectedField());
1986 final double actual = tc.getFunction().applyAsDouble(x, y);
1987 TestUtils.assertEquals(expected, actual, tc.getTolerance(), stats::add,
1988 () -> tc + " x=" + x + ", y=" + y);
1989 } catch (final NumberFormatException ex) {
1990 Assertions.fail("Failed to load data: " + Arrays.toString(in.getFields()), ex);
1991 }
1992 }
1993 } catch (final IOException ex) {
1994 Assertions.fail("Failed to load data: " + tc.getFilename(), ex);
1995 }
1996
1997 assertRms(tc, stats);
1998 }
1999
2000
2001
2002
2003
2004
2005
2006
2007 private static void assertRms(TestError te, TestUtils.ErrorStatistics stats) {
2008 final double rms = stats.getRMS();
2009
2010 Assertions.assertTrue(rms <= te.getRmsTolerance(),
2011 () -> String.format("%s RMS %s < %s", te, rms, te.getRmsTolerance()));
2012 }
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026 private static void debugRms(String name, double maxAbsUlp, double rmsUlp, double meanUlp, int size) {
2027
2028 System.out.printf("%-35s max %10.6g RMS %10.6g mean %14.6g n %4d%n",
2029 name, maxAbsUlp, rmsUlp, meanUlp, size);
2030 }
2031 }