Skip to content

Commit 1d670c8

Browse files
authored
Merge pull request #1346 from JacobHass8/asym-log-incomplete-gamma
Implement special function log incomplete gamma function
2 parents f6be8e8 + 2a6fb33 commit 1d670c8

File tree

10 files changed

+366
-6
lines changed

10 files changed

+366
-6
lines changed

doc/sf/igamma.qbk

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
template <class T1, class T2, class ``__Policy``>
2121
BOOST_MATH_GPU_ENABLED ``__sf_result`` gamma_q(T1 a, T2 z, const ``__Policy``&);
2222

23+
template <class T1, class T2>
24+
BOOST_MATH_GPU_ENABLED ``__sf_result`` lgamma_q(T1 a, T2 z);
25+
26+
template <class T1, class T2, class ``__Policy``>
27+
BOOST_MATH_GPU_ENABLED ``__sf_result`` lgamma_q(T1 a, T2 z, const ``__Policy``&);
28+
2329
template <class T1, class T2>
2430
BOOST_MATH_GPU_ENABLED ``__sf_result`` tgamma_lower(T1 a, T2 z);
2531

@@ -80,6 +86,15 @@ This function changes rapidly from 1 to 0 around the point z == a:
8086

8187
[graph gamma_q]
8288

89+
template <class T1, class T2>
90+
BOOST_MATH_GPU_ENABLED ``__sf_result`` lgamma_q(T1 a, T2 z);
91+
92+
template <class T1, class T2, class ``__Policy``>
93+
BOOST_MATH_GPU_ENABLED ``__sf_result`` lgamma_q(T1 a, T2 z, const ``__Policy``&);
94+
95+
Returns the natural log of the normalized upper incomplete gamma function
96+
of a and z.
97+
8398
template <class T1, class T2>
8499
BOOST_MATH_GPU_ENABLED ``__sf_result`` tgamma_lower(T1 a, T2 z);
85100

@@ -263,6 +278,16 @@ large a and x the errors will still get you eventually, although this does
263278
delay the inevitable much longer than other methods. Use of /log(1+x)-x/ here
264279
is inspired by Temme (see references below).
265280

281+
The natural log of the normalized upper incomplete gamma function is computed
282+
as expected except when the normalized upper incomplete gamma function
283+
begins to underflow. This approximately occurs at
284+
285+
((x > 1000) && ((a < x) || (fabs(a - 50) / x < 1))) || ((x > log_max_value<T>() - 10) && (x > a))
286+
287+
in which case an expansion, for large x, of the (non-normalised) upper
288+
incomplete gamma function is used. The return is then normalised by subtracting
289+
the log of the gamma function and adding /a log(x)-x-log(x)/.
290+
266291
[h4 References]
267292

268293
* N. M. Temme, A Set of Algorithms for the Incomplete Gamma Functions,

include/boost/math/special_functions/gamma.hpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,47 @@ BOOST_MATH_GPU_ENABLED T gamma_incomplete_imp(T a, T x, bool normalised, bool in
17721772
return gamma_incomplete_imp_final(T(a), T(x), normalised, invert, pol, p_derivative);
17731773
}
17741774

1775+
// Calculate log of incomplete gamma function
1776+
template <class T, class Policy>
1777+
BOOST_MATH_GPU_ENABLED T lgamma_incomplete_imp(T a, T x, const Policy& pol)
1778+
{
1779+
using namespace boost::math; // temporary until we're in the right namespace
1780+
1781+
BOOST_MATH_STD_USING_CORE
1782+
1783+
// Check for invalid inputs (a < 0 or x < 0)
1784+
constexpr auto function = "boost::math::lgamma_q<%1%>(%1%, %1%)";
1785+
if(a <= 0)
1786+
return policies::raise_domain_error<T>(function, "Argument a to the incomplete gamma function must be greater than zero (got a=%1%).", a, pol);
1787+
if(x < 0)
1788+
return policies::raise_domain_error<T>(function, "Argument x to the incomplete gamma function must be >= 0 (got x=%1%).", x, pol);
1789+
1790+
if (((x > 1000) || (x > tools::log_max_value<T>() - 10)) && (a + 50 < x))
1791+
{
1792+
//
1793+
// Take the logarithmic version of the asymtotic expansion:
1794+
//
1795+
return log(detail::incomplete_tgamma_large_x(a, x, pol)) + a * log(x) - x - lgamma(a, pol) - log(x);
1796+
}
1797+
//
1798+
// Can't do better than taking the log of Q, but...
1799+
//
1800+
// Figure out whether we need P or Q, since if we calculate Q and it's too close to unity
1801+
// we will lose precision in the result, selection logic here is extracted from gamma_incomplete_imp_final:
1802+
//
1803+
bool need_p = false;
1804+
if ((x < 0.5) && (T(-0.4) / log(x) < a))
1805+
need_p = true;
1806+
else if ((x < 1.1) && (x >= 0.5) && (x * 0.75f < a))
1807+
need_p = true;
1808+
else if ((x < a) && (x >= 1.1))
1809+
need_p = true;
1810+
1811+
if (need_p)
1812+
return log1p(-gamma_p(a, x, pol), pol);
1813+
return log(gamma_q(a, x, pol));
1814+
}
1815+
17751816
//
17761817
// Ratios of two gamma functions:
17771818
//
@@ -2390,6 +2431,29 @@ BOOST_MATH_GPU_ENABLED inline tools::promote_args_t<T1, T2>
23902431
{
23912432
return gamma_q(a, z, policies::policy<>());
23922433
}
2434+
2435+
template <class T1, class T2, class Policy>
2436+
BOOST_MATH_GPU_ENABLED inline tools::promote_args_t<T1, T2> lgamma_q(T1 a, T2 z, const Policy& /* pol */)
2437+
{
2438+
typedef tools::promote_args_t<T1, T2> result_type;
2439+
typedef typename policies::evaluation<result_type, Policy>::type value_type;
2440+
typedef typename policies::normalise<
2441+
Policy,
2442+
policies::promote_float<false>,
2443+
policies::promote_double<false>,
2444+
policies::discrete_quantile<>,
2445+
policies::assert_undefined<> >::type forwarding_policy;
2446+
2447+
return policies::checked_narrowing_cast<result_type, forwarding_policy>(
2448+
detail::lgamma_incomplete_imp(static_cast<value_type>(a),
2449+
static_cast<value_type>(z), forwarding_policy()), "lgamma_q<%1%>(%1%, %1%)");
2450+
}
2451+
2452+
template <class T1, class T2>
2453+
BOOST_MATH_GPU_ENABLED inline tools::promote_args_t<T1, T2> lgamma_q(T1 a, T2 z)
2454+
{
2455+
return lgamma_q(a, z, policies::policy<>());
2456+
}
23932457
//
23942458
// Regularised lower incomplete gamma:
23952459
//

include/boost/math/special_functions/math_fwd.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,12 @@ namespace boost
561561
template <class RT1, class RT2, class Policy>
562562
BOOST_MATH_GPU_ENABLED tools::promote_args_t<RT1, RT2> gamma_q(RT1 a, RT2 z, const Policy&);
563563

564+
template <class RT1, class RT2>
565+
BOOST_MATH_GPU_ENABLED tools::promote_args_t<RT1, RT2> lgamma_q(RT1 a, RT2 z);
566+
567+
template <class RT1, class RT2, class Policy>
568+
BOOST_MATH_GPU_ENABLED tools::promote_args_t<RT1, RT2> lgamma_q(RT1 a, RT2 z, const Policy&);
569+
564570
template <class RT1, class RT2>
565571
BOOST_MATH_GPU_ENABLED tools::promote_args_t<RT1, RT2> gamma_p(RT1 a, RT2 z);
566572

@@ -1516,6 +1522,9 @@ namespace boost
15161522
\
15171523
template <class RT1, class RT2>\
15181524
BOOST_MATH_GPU_ENABLED inline boost::math::tools::promote_args_t<RT1, RT2> gamma_q(RT1 a, RT2 z){ return boost::math::gamma_q(a, z, Policy()); }\
1525+
\
1526+
template <class RT1, class RT2>\
1527+
BOOST_MATH_GPU_ENABLED inline boost::math::tools::promote_args_t<RT1, RT2> lgamma_q(RT1 a, RT2 z){ return boost::math::lgamma_q(a, z, Policy()); }\
15191528
\
15201529
template <class RT1, class RT2>\
15211530
BOOST_MATH_GPU_ENABLED inline boost::math::tools::promote_args_t<RT1, RT2> gamma_p(RT1 a, RT2 z){ return boost::math::gamma_p(a, z, Policy()); }\

test/compile_test/instantiate.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ void instantiate(RealType)
263263
boost::math::tgamma_lower(v1, v2);
264264
boost::math::gamma_p(v1, v2);
265265
boost::math::gamma_q(v1, v2);
266+
boost::math::lgamma_q(v1, v2);
266267
boost::math::gamma_p_inv(v1, v2);
267268
boost::math::gamma_q_inv(v1, v2);
268269
boost::math::gamma_p_inva(v1, v2);
@@ -542,6 +543,7 @@ void instantiate(RealType)
542543
boost::math::tgamma_lower(v1 * 1, v2 - 0);
543544
boost::math::gamma_p(v1 * 1, v2 + 0);
544545
boost::math::gamma_q(v1 * 1, v2 + 0);
546+
boost::math::lgamma_q(v1 * 1, v2 + 0);
545547
boost::math::gamma_p_inv(v1 * 1, v2 + 0);
546548
boost::math::gamma_q_inv(v1 * 1, v2 + 0);
547549
boost::math::gamma_p_inva(v1 * 1, v2 + 0);
@@ -793,6 +795,7 @@ void instantiate(RealType)
793795
boost::math::tgamma_lower(v1, v2, pol);
794796
boost::math::gamma_p(v1, v2, pol);
795797
boost::math::gamma_q(v1, v2, pol);
798+
boost::math::lgamma_q(v1, v2, pol);
796799
boost::math::gamma_p_inv(v1, v2, pol);
797800
boost::math::gamma_q_inv(v1, v2, pol);
798801
boost::math::gamma_p_inva(v1, v2, pol);
@@ -1070,6 +1073,7 @@ void instantiate(RealType)
10701073
test::tgamma_lower(v1, v2);
10711074
test::gamma_p(v1, v2);
10721075
test::gamma_q(v1, v2);
1076+
test::lgamma_q(v1, v2);
10731077
test::gamma_p_inv(v1, v2);
10741078
test::gamma_q_inv(v1, v2);
10751079
test::gamma_p_inva(v1, v2);
@@ -1351,6 +1355,7 @@ void instantiate_mixed(RealType)
13511355
boost::math::gamma_p(i, s);
13521356
boost::math::gamma_p(fr, lr);
13531357
boost::math::gamma_q(i, s);
1358+
boost::math::lgamma_q(i, s);
13541359
boost::math::gamma_q(fr, lr);
13551360
boost::math::gamma_p_inv(i, fr);
13561361
boost::math::gamma_q_inv(s, fr);
@@ -1566,6 +1571,7 @@ void instantiate_mixed(RealType)
15661571
boost::math::gamma_p(i, s, pol);
15671572
boost::math::gamma_p(fr, lr, pol);
15681573
boost::math::gamma_q(i, s, pol);
1574+
boost::math::lgamma_q(i, s, pol);
15691575
boost::math::gamma_q(fr, lr, pol);
15701576
boost::math::gamma_p_inv(i, fr, pol);
15711577
boost::math::gamma_q_inv(s, fr, pol);
@@ -1777,7 +1783,9 @@ void instantiate_mixed(RealType)
17771783
test::gamma_p(i, s);
17781784
test::gamma_p(fr, lr);
17791785
test::gamma_q(i, s);
1786+
test::lgamma_q(i, s);
17801787
test::gamma_q(fr, lr);
1788+
test::lgamma_q(fr, lr);
17811789
test::gamma_p_inv(i, fr);
17821790
test::gamma_q_inv(s, fr);
17831791
test::gamma_p_inva(i, lr);

test/compile_test/sf_gamma_incl_test.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ void compile_and_link_test()
3939
check_result<long double>(boost::math::gamma_q<long double>(l, l));
4040
#endif
4141

42+
check_result<float>(boost::math::lgamma_q<float>(f, f));
43+
check_result<double>(boost::math::lgamma_q<double>(d, d));
44+
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
45+
check_result<long double>(boost::math::lgamma_q<long double>(l, l));
46+
#endif
47+
4248
check_result<float>(boost::math::gamma_p_inv<float>(f, f));
4349
check_result<double>(boost::math::gamma_p_inv<double>(d, d));
4450
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS

test/cuda_jamfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ run test_gamma_p_derivative_double.cu ;
369369
run test_gamma_p_derivative_float.cu ;
370370
run test_gamma_p_inv_double.cu ;
371371
run test_gamma_p_inv_float.cu ;
372+
run test_lgamma_q_double.cu ;
373+
run test_lgamma_q_float.cu ;
372374

373375
run test_log1p_double.cu ;
374376
run test_log1p_float.cu ;

test/test_igamma.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,13 @@ BOOST_AUTO_TEST_CASE( test_main )
394394
BOOST_MATH_CONTROL_FP;
395395

396396
#ifndef BOOST_MATH_BUGGY_LARGE_FLOAT_CONSTANTS
397-
test_spots(0.0F);
397+
test_spots(0.0F, "float");
398398
#endif
399-
test_spots(0.0);
399+
test_spots(0.0, "double");
400400
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
401-
test_spots(0.0L);
401+
test_spots(0.0L, "long double");
402402
#ifndef BOOST_MATH_NO_REAL_CONCEPT_TESTS
403-
test_spots(boost::math::concepts::real_concept(0.1));
403+
test_spots(boost::math::concepts::real_concept(0.1), "real_concept");
404404
#endif
405405
#endif
406406

test/test_igamma.hpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include <boost/type_traits/is_floating_point.hpp>
1919
#include <boost/array.hpp>
2020
#include "functor.hpp"
21-
2221
#include "handle_test_result.hpp"
2322
#include "table_type.hpp"
2423

@@ -141,8 +140,9 @@ void test_gamma(T, const char* name)
141140
}
142141

143142
template <class T>
144-
void test_spots(T)
143+
void test_spots(T, const char* name = nullptr)
145144
{
145+
std::cout << "Testing spot values with type " << name << std::endl;
146146
//
147147
// basic sanity checks, tolerance is 10 epsilon expressed as a percentage:
148148
//
@@ -256,6 +256,38 @@ void test_spots(T)
256256
BOOST_CHECK_EQUAL(::boost::math::gamma_q(static_cast<T>(1770), static_cast<T>(1e-12)), 1);
257257
BOOST_CHECK_EQUAL(::boost::math::gamma_p(static_cast<T>(1770), static_cast<T>(1e-12)), 0);
258258
//
259+
// Check that lgamma_q returns correct values with spot values calculated via wolframalpha log(Q[a, x])
260+
//
261+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(5), static_cast<T>(100)), static_cast<T>(-84.71697591169848944613823640968965801339401810393519310714864307L), tolerance);
262+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(22.5), static_cast<T>(2000)), static_cast<T>(-1883.489773203771543025750308264545743305089849873060383828767138L), tolerance);
263+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(501.25), static_cast<T>(2000)), static_cast<T>(-810.2453406781655559126505101822969531699112391075198076300675402L), tolerance);
264+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(20), static_cast<T>(0.25)), static_cast<T>(-2.946458104491857816330873290969917497748067639461638294404e-31L), tolerance);
265+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(40), static_cast<T>(0.75)), static_cast<T>(-5.930604927955460343652485525435087275997461623988991819824e-54L), tolerance);
266+
#if defined(__CYGWIN__) || defined(__MINGW32__)
267+
T gcc_win_mul = 2;
268+
#else
269+
T gcc_win_mul = 1;
270+
#endif
271+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(50), static_cast<T>(2)), static_cast<T>(-5.214301903317168085381693412994550732094621576607843973832e-51L), tolerance * gcc_win_mul);
272+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(500), static_cast<T>(10)), static_cast<T>(-3.79666711621207197039397438773960431648625558027046365463e-639L), tolerance * 3 * gcc_win_mul);
273+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(5), static_cast<T>(1000)), static_cast<T>(-975.5430287171020511929200293377669175923128826278957569928895945L), tolerance);
274+
// Pairs of tests that bisect the crossover condition in our code at double and then quad precision:
275+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(10), static_cast<T>(698.75)), static_cast<T>(-652.5952453102824132865663191324423994628428404928732148525545721L), tolerance);
276+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(10), static_cast<T>(699.25)), static_cast<T>(-653.0888168445921483147208556398158677077537551419551652934287016L), tolerance);
277+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(10), static_cast<T>(999.75)), static_cast<T>(-950.3752463850600415679327136010171192193400042422096029239012176L), tolerance);
278+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(10), static_cast<T>(1000.25)), static_cast<T>(-950.8707509166152482936275883547176592196662090187561681198668099L), tolerance);
279+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(50), static_cast<T>(698.75)), static_cast<T>(-522.3277960730837166223131189587863209730608668858212533099139269L), tolerance);
280+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(50), static_cast<T>(699.25)), static_cast<T>(-522.7927997457481265511084805522296021540768033975669071565674196L), tolerance);
281+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(50), static_cast<T>(999.75)), static_cast<T>(-805.7977867938474339107474131612354353193501692041340771552419902L), tolerance);
282+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(50), static_cast<T>(1000.25)), static_cast<T>(-806.2733124989172792095030711884568388681331032891850662521501582L), tolerance);
283+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(800), static_cast<T>(999.75)), static_cast<T>(-24.33274293617739453303937714319703386675839030466670622049929011L), tolerance * 2);
284+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(800), static_cast<T>(1000.25)), static_cast<T>(-24.43514173634027477093666725985191846106997808357863808910970142L), tolerance * (boost::math::tools::digits<T>() > 54 ? 20 : 1));
285+
// Once we get large a,x then error start to accumulate no matter what we do:
286+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(1200), static_cast<T>(1249.75)), static_cast<T>(-2.565496161584661216769813239648606145255794643472303927896044375L), tolerance * (std::is_floating_point<T>::value ? 1 : 4));
287+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(1200), static_cast<T>(1250.25)), static_cast<T>(-2.591934862117586205519309712218581885256650074210410262843591453L), tolerance * ((std::numeric_limits<T>::max_digits10 >= 36 || std::is_same<T, boost::math::concepts::real_concept>::value) ? 750 : (std::is_same<T, float>::value ? 1 : 50))); // Test fails on ARM64 and s390x long doubles and real_concept types unless tolerance is adjusted
288+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(2200), static_cast<T>(2249.75)), static_cast<T>(-1.933779894897391651410597618307863427927461116308937004149240320L), tolerance * (std::is_floating_point<T>::value ? 1 : 10));
289+
BOOST_CHECK_CLOSE(::boost::math::lgamma_q(static_cast<T>(2200), static_cast<T>(2250.25)), static_cast<T>(-1.950346484067948344620463026377077515919992808640737320057812268L), tolerance * (std::is_same<T, float>::value ? 1 : (std::is_floating_point<T>::value ? 100 : 200)));
290+
//
259291
// Coverage:
260292
//
261293
#ifndef BOOST_MATH_NO_EXCEPTIONS
@@ -265,6 +297,11 @@ void test_spots(T)
265297
BOOST_CHECK_THROW(boost::math::gamma_q(static_cast<T>(1), static_cast<T>(-2)), std::domain_error);
266298
BOOST_CHECK_THROW(boost::math::gamma_p(static_cast<T>(0), static_cast<T>(2)), std::domain_error);
267299
BOOST_CHECK_THROW(boost::math::gamma_q(static_cast<T>(0), static_cast<T>(2)), std::domain_error);
300+
301+
BOOST_CHECK_THROW(boost::math::lgamma_q(static_cast<T>(-1), static_cast<T>(2)), std::domain_error);
302+
BOOST_CHECK_THROW(boost::math::lgamma_q(static_cast<T>(1), static_cast<T>(-2)), std::domain_error);
303+
BOOST_CHECK_THROW(boost::math::lgamma_q(static_cast<T>(0), static_cast<T>(2)), std::domain_error);
304+
268305
BOOST_CHECK_THROW(boost::math::gamma_p_derivative(static_cast<T>(-1), static_cast<T>(2)), std::domain_error);
269306
BOOST_CHECK_THROW(boost::math::gamma_p_derivative(static_cast<T>(1), static_cast<T>(-2)), std::domain_error);
270307
BOOST_CHECK_THROW(boost::math::gamma_p_derivative(static_cast<T>(0), static_cast<T>(2)), std::domain_error);
@@ -275,6 +312,11 @@ void test_spots(T)
275312
BOOST_CHECK((boost::math::isnan)(boost::math::gamma_q(static_cast<T>(1), static_cast<T>(-2))));
276313
BOOST_CHECK((boost::math::isnan)(boost::math::gamma_p(static_cast<T>(0), static_cast<T>(2))));
277314
BOOST_CHECK((boost::math::isnan)(boost::math::gamma_q(static_cast<T>(0), static_cast<T>(2))));
315+
316+
BOOST_CHECK((boost::math::isnan)(boost::math::lgamma_q(static_cast<T>(-1), static_cast<T>(2))));
317+
BOOST_CHECK((boost::math::isnan)(boost::math::lgamma_q(static_cast<T>(1), static_cast<T>(-2))));
318+
BOOST_CHECK((boost::math::isnan)(boost::math::lgamma_q(static_cast<T>(0), static_cast<T>(2))));
319+
278320
BOOST_CHECK((boost::math::isnan)(boost::math::gamma_p_derivative(static_cast<T>(-1), static_cast<T>(2))));
279321
BOOST_CHECK((boost::math::isnan)(boost::math::gamma_p_derivative(static_cast<T>(1), static_cast<T>(-2))));
280322
BOOST_CHECK((boost::math::isnan)(boost::math::gamma_p_derivative(static_cast<T>(0), static_cast<T>(2))));

0 commit comments

Comments
 (0)