diff --git a/include/boost/math/special_functions/gamma.hpp b/include/boost/math/special_functions/gamma.hpp index 458dc40a3d..267e58620d 100644 --- a/include/boost/math/special_functions/gamma.hpp +++ b/include/boost/math/special_functions/gamma.hpp @@ -1290,7 +1290,6 @@ BOOST_MATH_GPU_ENABLED T incomplete_tgamma_large_x(const T& a, const T& x, const return result; } - // // Main incomplete gamma entry point, handles all four incomplete gamma's: // @@ -1813,6 +1812,45 @@ BOOST_MATH_GPU_ENABLED T lgamma_incomplete_imp(T a, T x, const Policy& pol) return log(gamma_q(a, x, pol)); } +// Calculate log of incomplete gamma function +template +BOOST_MATH_GPU_ENABLED T lgamma_incomplete_lower_imp(T a, T x, const Policy& pol) +{ + using namespace boost::math; // temporary until we're in the right namespace + + BOOST_MATH_STD_USING_CORE + + // Check for invalid inputs (a < 0 or x < 0) + constexpr auto function = "boost::math::lgamma_p<%1%>(%1%, %1%)"; + if(a <= 0) + return policies::raise_domain_error(function, "Argument a to the incomplete gamma function must be greater than zero (got a=%1%).", a, pol); + if(x < 0) + return policies::raise_domain_error(function, "Argument x to the incomplete gamma function must be >= 0 (got x=%1%).", x, pol); + + // Taken from conditions on Line 1354, 1368, 1709 + if (((a > 4 * x) && (a >= max_factorial::value)) || (T(-0.4) / log(x) < a) || ((x * 0.75f < a) && x < T(1.1))){ + return log(detail::lower_gamma_series(a, x, pol)) - log(a) + a * log(x) - x - lgamma(a, pol); + } + + // + // Can't do better than taking the log of P, but... + // + // Figure out whether we need P or Q, since if we calculate P and it's too close to unity + // we will lose precision in the result, selection logic here is extracted from gamma_incomplete_imp_final: + // + bool need_p = false; + if ((x < 0.5) && (T(-0.4) / log(x) < a)) + need_p = true; + else if ((x < 1.1) && (x >= 0.5) && (x * 0.75f < a)) + need_p = true; + else if ((x < a) && (x >= 1.1)) + need_p = true; + + if (need_p) + return log(gamma_p(a, x, pol)); + return log1p(-gamma_q(a, x, pol), pol); +} + // // Ratios of two gamma functions: // @@ -2454,6 +2492,29 @@ BOOST_MATH_GPU_ENABLED inline tools::promote_args_t lgamma_q(T1 a, T2 z) { return lgamma_q(a, z, policies::policy<>()); } + +template +BOOST_MATH_GPU_ENABLED inline tools::promote_args_t lgamma_p(T1 a, T2 z, const Policy& /* pol */) +{ + typedef tools::promote_args_t result_type; + typedef typename policies::evaluation::type value_type; + typedef typename policies::normalise< + Policy, + policies::promote_float, + policies::promote_double, + policies::discrete_quantile<>, + policies::assert_undefined<> >::type forwarding_policy; + + return policies::checked_narrowing_cast( + detail::lgamma_incomplete_lower_imp(static_cast(a), + static_cast(z), forwarding_policy()), "lgamma_p<%1%>(%1%, %1%)"); +} + +template +BOOST_MATH_GPU_ENABLED inline tools::promote_args_t lgamma_p(T1 a, T2 z) +{ + return lgamma_p(a, z, policies::policy<>()); +} // // Regularised lower incomplete gamma: // diff --git a/include/boost/math/special_functions/math_fwd.hpp b/include/boost/math/special_functions/math_fwd.hpp index ca16fc8d96..6c8394ddf9 100644 --- a/include/boost/math/special_functions/math_fwd.hpp +++ b/include/boost/math/special_functions/math_fwd.hpp @@ -567,6 +567,12 @@ namespace boost template BOOST_MATH_GPU_ENABLED tools::promote_args_t lgamma_q(RT1 a, RT2 z, const Policy&); + template + BOOST_MATH_GPU_ENABLED tools::promote_args_t lgamma_p(RT1 a, RT2 z); + + template + BOOST_MATH_GPU_ENABLED tools::promote_args_t lgamma_p(RT1 a, RT2 z, const Policy&); + template BOOST_MATH_GPU_ENABLED tools::promote_args_t gamma_p(RT1 a, RT2 z); @@ -1525,6 +1531,9 @@ namespace boost \ template \ BOOST_MATH_GPU_ENABLED inline boost::math::tools::promote_args_t lgamma_q(RT1 a, RT2 z){ return boost::math::lgamma_q(a, z, Policy()); }\ +\ + template \ + BOOST_MATH_GPU_ENABLED inline boost::math::tools::promote_args_t lgamma_p(RT1 a, RT2 z){ return boost::math::lgamma_p(a, z, Policy()); }\ \ template \ BOOST_MATH_GPU_ENABLED inline boost::math::tools::promote_args_t gamma_p(RT1 a, RT2 z){ return boost::math::gamma_p(a, z, Policy()); }\