// boost\math\distributions\non_central_chi_squared.hpp // Copyright John Maddock 2008. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP #define BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP #include #include // for incomplete gamma. gamma_q #include // for cyl_bessel_i #include // for iround #include // complements #include // central distribution #include // error checks #include // isnan. #include // for root finding. #include #include namespace boost { namespace math { template class non_central_chi_squared_distribution; namespace detail{ template T non_central_chi_square_q(T x, T f, T theta, const Policy& pol, T init_sum = 0) { // // Computes the complement of the Non-Central Chi-Square // Distribution CDF by summing a weighted sum of complements // of the central-distributions. The weighting factor is // a Poisson Distribution. // // This is an application of the technique described in: // // Computing discrete mixtures of continuous // distributions: noncentral chisquare, noncentral t // and the distribution of the square of the sample // multiple correlation coeficient. // D. Benton, K. Krishnamoorthy. // Computational Statistics & Data Analysis 43 (2003) 249 - 267 // BOOST_MATH_STD_USING // Special case: if(x == 0) return 1; // // Initialize the variables we'll be using: // T lambda = theta / 2; T del = f / 2; T y = x / 2; boost::uintmax_t max_iter = policies::get_max_series_iterations(); T errtol = boost::math::policies::get_epsilon(); T sum = init_sum; // // k is the starting location for iteration, we'll // move both forwards and backwards from this point. // k is chosen as the peek of the Poisson weights, which // will occur *before* the largest term. // int k = iround(lambda, pol); // Forwards and backwards Poisson weights: T poisf = boost::math::gamma_p_derivative(1 + k, lambda, pol); T poisb = poisf * k / lambda; // Initial forwards central chi squared term: T gamf = boost::math::gamma_q(del + k, y, pol); // Forwards and backwards recursion terms on the central chi squared: T xtermf = boost::math::gamma_p_derivative(del + 1 + k, y, pol); T xtermb = xtermf * (del + k) / y; // Initial backwards central chi squared term: T gamb = gamf - xtermb; // // Forwards iteration first, this is the // stable direction for the gamma function // recurrences: // int i; for(i = k; static_cast(i-k) < max_iter; ++i) { T term = poisf * gamf; sum += term; poisf *= lambda / (i + 1); gamf += xtermf; xtermf *= y / (del + i + 1); if(((sum == 0) || (fabs(term / sum) < errtol)) && (term >= poisf * gamf)) break; } //Error check: if(static_cast(i-k) >= max_iter) return policies::raise_evaluation_error( "cdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); // // Now backwards iteration: the gamma // function recurrences are unstable in this // direction, we rely on the terms deminishing in size // faster than we introduce cancellation errors. // For this reason it's very important that we start // *before* the largest term so that backwards iteration // is strictly converging. // for(i = k - 1; i >= 0; --i) { T term = poisb * gamb; sum += term; poisb *= i / lambda; xtermb *= (del + i) / y; gamb -= xtermb; if((sum == 0) || (fabs(term / sum) < errtol)) break; } return sum; } template T non_central_chi_square_p_ding(T x, T f, T theta, const Policy& pol, T init_sum = 0) { // // This is an implementation of: // // Algorithm AS 275: // Computing the Non-Central #2 Distribution Function // Cherng G. Ding // Applied Statistics, Vol. 41, No. 2. (1992), pp. 478-482. // // This uses a stable forward iteration to sum the // CDF, unfortunately this can not be used for large // values of the non-centrality parameter because: // * The first term may underfow to zero. // * We may need an extra-ordinary number of terms // before we reach the first *significant* term. // BOOST_MATH_STD_USING // Special case: if(x == 0) return 0; T tk = boost::math::gamma_p_derivative(f/2 + 1, x/2, pol); T lambda = theta / 2; T vk = exp(-lambda); T uk = vk; T sum = init_sum + tk * vk; if(sum == 0) return sum; boost::uintmax_t max_iter = policies::get_max_series_iterations(); T errtol = boost::math::policies::get_epsilon(); int i; T lterm(0), term(0); for(i = 1; static_cast(i) < max_iter; ++i) { tk = tk * x / (f + 2 * i); uk = uk * lambda / i; vk = vk + uk; lterm = term; term = vk * tk; sum += term; if((fabs(term / sum) < errtol) && (term <= lterm)) break; } //Error check: if(static_cast(i) >= max_iter) return policies::raise_evaluation_error( "cdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); return sum; } template T non_central_chi_square_p(T y, T n, T lambda, const Policy& pol, T init_sum) { // // This is taken more or less directly from: // // Computing discrete mixtures of continuous // distributions: noncentral chisquare, noncentral t // and the distribution of the square of the sample // multiple correlation coeficient. // D. Benton, K. Krishnamoorthy. // Computational Statistics & Data Analysis 43 (2003) 249 - 267 // // We're summing a Poisson weighting term multiplied by // a central chi squared distribution. // BOOST_MATH_STD_USING // Special case: if(y == 0) return 0; boost::uintmax_t max_iter = policies::get_max_series_iterations(); T errtol = boost::math::policies::get_epsilon(); T errorf(0), errorb(0); T x = y / 2; T del = lambda / 2; // // Starting location for the iteration, we'll iterate // both forwards and backwards from this point. The // location chosen is the maximum of the Poisson weight // function, which ocurrs *after* the largest term in the // sum. // int k = iround(del, pol); T a = n / 2 + k; // Central chi squared term for forward iteration: T gamkf = boost::math::gamma_p(a, x, pol); if(lambda == 0) return gamkf; // Central chi squared term for backward iteration: T gamkb = gamkf; // Forwards Poisson weight: T poiskf = gamma_p_derivative(k+1, del, pol); // Backwards Poisson weight: T poiskb = poiskf; // Forwards gamma function recursion term: T xtermf = boost::math::gamma_p_derivative(a, x, pol); // Backwards gamma function recursion term: T xtermb = xtermf * x / a; T sum = init_sum + poiskf * gamkf; if(sum == 0) return sum; int i = 1; // // Backwards recursion first, this is the stable // direction for gamma function recurrences: // while(i <= k) { xtermb *= (a - i + 1) / x; gamkb += xtermb; poiskb = poiskb * (k - i + 1) / del; errorf = errorb; errorb = gamkb * poiskb; sum += errorb; if((fabs(errorb / sum) < errtol) && (errorb <= errorf)) break; ++i; } i = 1; // // Now forwards recursion, the gamma function // recurrence relation is unstable in this direction, // so we rely on the magnitude of successive terms // decreasing faster than we introduce cancellation error. // For this reason it's vital that k is chosen to be *after* // the largest term, so that successive forward iterations // are strictly (and rapidly) converging. // do { xtermf = xtermf * x / (a + i - 1); gamkf = gamkf - xtermf; poiskf = poiskf * del / (k + i); errorf = poiskf * gamkf; sum += errorf; ++i; }while((fabs(errorf / sum) > errtol) && (static_cast(i) < max_iter)); //Error check: if(static_cast(i) >= max_iter) return policies::raise_evaluation_error( "cdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); return sum; } template T non_central_chi_square_pdf(T x, T n, T lambda, const Policy& pol) { // // As above but for the PDF: // BOOST_MATH_STD_USING boost::uintmax_t max_iter = policies::get_max_series_iterations(); T errtol = boost::math::policies::get_epsilon(); T x2 = x / 2; T n2 = n / 2; T l2 = lambda / 2; T sum = 0; int k = itrunc(l2); T pois = gamma_p_derivative(k + 1, l2, pol) * gamma_p_derivative(n2 + k, x2); if(pois == 0) return 0; T poisb = pois; for(int i = k; ; ++i) { sum += pois; if(pois / sum < errtol) break; if(static_cast(i - k) >= max_iter) return policies::raise_evaluation_error( "pdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); pois *= l2 * x2 / ((i + 1) * (n2 + i)); } for(int i = k - 1; i >= 0; --i) { poisb *= (i + 1) * (n2 + i) / (l2 * x2); sum += poisb; if(poisb / sum < errtol) break; } return sum / 2; } template inline RealType non_central_chi_squared_cdf(RealType x, RealType k, RealType l, bool invert, const Policy&) { 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; BOOST_MATH_STD_USING value_type result; if(l == 0) result = cdf(boost::math::chi_squared_distribution(k), x); else if(x > k + l) { // Complement is the smaller of the two: result = detail::non_central_chi_square_q( static_cast(x), static_cast(k), static_cast(l), forwarding_policy(), static_cast(invert ? 0 : -1)); invert = !invert; } else if(l < 200) { // For small values of the non-centrality parameter // we can use Ding's method: result = detail::non_central_chi_square_p_ding( static_cast(x), static_cast(k), static_cast(l), forwarding_policy(), static_cast(invert ? -1 : 0)); } else { // For largers values of the non-centrality // parameter Ding's method will consume an // extra-ordinary number of terms, and worse // may return zero when the result is in fact // finite, use Krishnamoorthy's method instead: result = detail::non_central_chi_square_p( static_cast(x), static_cast(k), static_cast(l), forwarding_policy(), static_cast(invert ? -1 : 0)); } if(invert) result = -result; return policies::checked_narrowing_cast( result, "boost::math::non_central_chi_squared_cdf<%1%>(%1%, %1%, %1%)"); } template struct nccs_quantile_functor { nccs_quantile_functor(const non_central_chi_squared_distribution& d, T t, bool c) : dist(d), target(t), comp(c) {} T operator()(const T& x) { return comp ? target - cdf(complement(dist, x)) : cdf(dist, x) - target; } private: non_central_chi_squared_distribution dist; T target; bool comp; }; template RealType nccs_quantile(const non_central_chi_squared_distribution& dist, const RealType& p, bool comp) { BOOST_MATH_STD_USING static const char* function = "quantile(non_central_chi_squared_distribution<%1%>, %1%)"; 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; value_type k = dist.degrees_of_freedom(); value_type l = dist.non_centrality(); value_type r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy()) || !detail::check_probability( function, static_cast(p), &r, Policy())) return (RealType)r; // // Special cases get short-circuited first: // if(p == 0) return comp ? policies::raise_overflow_error(function, 0, Policy()) : 0; if(p == 1) return comp ? 0 : policies::raise_overflow_error(function, 0, Policy()); // // This is Pearson's approximation to the quantile, see // Pearson, E. S. (1959) "Note on an approximation to the distribution of // noncentral chi squared", Biometrika 46: 364. // See also: // "A comparison of approximations to percentiles of the noncentral chi2-distribution", // Hardeo Sahai and Mario Miguel Ojeda, Revista de Matematica: Teoria y Aplicaciones 2003 10(1–2) : 57–76. // Note that the latter reference refers to an approximation of the CDF, when they really mean the quantile. // value_type b = -(l * l) / (k + 3 * l); value_type c = (k + 3 * l) / (k + 2 * l); value_type ff = (k + 2 * l) / (c * c); value_type guess; if(comp) { guess = b + c * quantile(complement(chi_squared_distribution(ff), p)); } else { guess = b + c * quantile(chi_squared_distribution(ff), p); } // // Sometimes guess goes very small or negative, in that case we have // to do something else for the initial guess, this approximation // was provided in a private communication from Thomas Luu, PhD candidate, // University College London. It's an asymptotic expansion for the // quantile which usually gets us within an order of magnitude of the // correct answer. // if(guess < 0.005) { value_type pp = comp ? 1 - p : p; //guess = pow(pow(value_type(2), (k / 2 - 1)) * exp(l / 2) * pp * k, 2 / k); guess = pow(pow(value_type(2), (k / 2 - 1)) * exp(l / 2) * pp * k * boost::math::tgamma(k / 2, forwarding_policy()), (2 / k)); if(guess == 0) guess = tools::min_value(); } value_type result = detail::generic_quantile( non_central_chi_squared_distribution(k, l), p, guess, comp, function); return policies::checked_narrowing_cast( result, function); } template RealType nccs_pdf(const non_central_chi_squared_distribution& dist, const RealType& x) { BOOST_MATH_STD_USING static const char* function = "pdf(non_central_chi_squared_distribution<%1%>, %1%)"; 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; value_type k = dist.degrees_of_freedom(); value_type l = dist.non_centrality(); value_type r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy()) || !detail::check_positive_x( function, (value_type)x, &r, Policy())) return (RealType)r; if(l == 0) return pdf(boost::math::chi_squared_distribution(dist.degrees_of_freedom()), x); // Special case: if(x == 0) return 0; if(l > 50) { r = non_central_chi_square_pdf(static_cast(x), k, l, forwarding_policy()); } else { r = log(x / l) * (k / 4 - 0.5f) - (x + l) / 2; if(fabs(r) >= tools::log_max_value() / 4) { r = non_central_chi_square_pdf(static_cast(x), k, l, forwarding_policy()); } else { r = exp(r); r = 0.5f * r * boost::math::cyl_bessel_i(k/2 - 1, sqrt(l * x), forwarding_policy()); } } return policies::checked_narrowing_cast( r, function); } template struct degrees_of_freedom_finder { degrees_of_freedom_finder( RealType lam_, RealType x_, RealType p_, bool c) : lam(lam_), x(x_), p(p_), comp(c) {} RealType operator()(const RealType& v) { non_central_chi_squared_distribution d(v, lam); return comp ? RealType(p - cdf(complement(d, x))) : RealType(cdf(d, x) - p); } private: RealType lam; RealType x; RealType p; bool comp; }; template inline RealType find_degrees_of_freedom( RealType lam, RealType x, RealType p, RealType q, const Policy& pol) { const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom"; if((p == 0) || (q == 0)) { // // Can't a thing if one of p and q is zero: // return policies::raise_evaluation_error(function, "Can't find degrees of freedom when the probability is 0 or 1, only possible answer is %1%", RealType(std::numeric_limits::quiet_NaN()), Policy()); } degrees_of_freedom_finder f(lam, x, p < q ? p : q, p < q ? false : true); tools::eps_tolerance tol(policies::digits()); boost::uintmax_t max_iter = policies::get_max_root_iterations(); // // Pick an initial guess that we know will give us a probability // right around 0.5. // RealType guess = x - lam; if(guess < 1) guess = 1; std::pair ir = tools::bracket_and_solve_root( f, guess, RealType(2), false, tol, max_iter, pol); RealType result = ir.first + (ir.second - ir.first) / 2; if(max_iter >= policies::get_max_root_iterations()) { return policies::raise_evaluation_error(function, "Unable to locate solution in a reasonable time:" " or there is no answer to problem. Current best guess is %1%", result, Policy()); } return result; } template struct non_centrality_finder { non_centrality_finder( RealType v_, RealType x_, RealType p_, bool c) : v(v_), x(x_), p(p_), comp(c) {} RealType operator()(const RealType& lam) { non_central_chi_squared_distribution d(v, lam); return comp ? RealType(p - cdf(complement(d, x))) : RealType(cdf(d, x) - p); } private: RealType v; RealType x; RealType p; bool comp; }; template inline RealType find_non_centrality( RealType v, RealType x, RealType p, RealType q, const Policy& pol) { const char* function = "non_central_chi_squared<%1%>::find_non_centrality"; if((p == 0) || (q == 0)) { // // Can't do a thing if one of p and q is zero: // return policies::raise_evaluation_error(function, "Can't find non centrality parameter when the probability is 0 or 1, only possible answer is %1%", RealType(std::numeric_limits::quiet_NaN()), Policy()); } non_centrality_finder f(v, x, p < q ? p : q, p < q ? false : true); tools::eps_tolerance tol(policies::digits()); boost::uintmax_t max_iter = policies::get_max_root_iterations(); // // Pick an initial guess that we know will give us a probability // right around 0.5. // RealType guess = x - v; if(guess < 1) guess = 1; std::pair ir = tools::bracket_and_solve_root( f, guess, RealType(2), false, tol, max_iter, pol); RealType result = ir.first + (ir.second - ir.first) / 2; if(max_iter >= policies::get_max_root_iterations()) { return policies::raise_evaluation_error(function, "Unable to locate solution in a reasonable time:" " or there is no answer to problem. Current best guess is %1%", result, Policy()); } return result; } } template > class non_central_chi_squared_distribution { public: typedef RealType value_type; typedef Policy policy_type; non_central_chi_squared_distribution(RealType df_, RealType lambda) : df(df_), ncp(lambda) { const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::non_central_chi_squared_distribution(%1%,%1%)"; RealType r; detail::check_df( function, df, &r, Policy()); detail::check_non_centrality( function, ncp, &r, Policy()); } // non_central_chi_squared_distribution constructor. RealType degrees_of_freedom() const { // Private data getter function. return df; } RealType non_centrality() const { // Private data getter function. return ncp; } static RealType find_degrees_of_freedom(RealType lam, RealType x, RealType p) { const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom"; 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; value_type result = detail::find_degrees_of_freedom( static_cast(lam), static_cast(x), static_cast(p), static_cast(1-p), forwarding_policy()); return policies::checked_narrowing_cast( result, function); } template static RealType find_degrees_of_freedom(const complemented3_type& c) { const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom"; 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; value_type result = detail::find_degrees_of_freedom( static_cast(c.dist), static_cast(c.param1), static_cast(1-c.param2), static_cast(c.param2), forwarding_policy()); return policies::checked_narrowing_cast( result, function); } static RealType find_non_centrality(RealType v, RealType x, RealType p) { const char* function = "non_central_chi_squared<%1%>::find_non_centrality"; 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; value_type result = detail::find_non_centrality( static_cast(v), static_cast(x), static_cast(p), static_cast(1-p), forwarding_policy()); return policies::checked_narrowing_cast( result, function); } template static RealType find_non_centrality(const complemented3_type& c) { const char* function = "non_central_chi_squared<%1%>::find_non_centrality"; 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; value_type result = detail::find_non_centrality( static_cast(c.dist), static_cast(c.param1), static_cast(1-c.param2), static_cast(c.param2), forwarding_policy()); return policies::checked_narrowing_cast( result, function); } private: // Data member, initialized by constructor. RealType df; // degrees of freedom. RealType ncp; // non-centrality parameter }; // template class non_central_chi_squared_distribution typedef non_central_chi_squared_distribution non_central_chi_squared; // Reserved name of type double. // Non-member functions to give properties of the distribution. template inline const std::pair range(const non_central_chi_squared_distribution& /* dist */) { // Range of permissible values for random variable k. using boost::math::tools::max_value; return std::pair(static_cast(0), max_value()); // Max integer? } template inline const std::pair support(const non_central_chi_squared_distribution& /* dist */) { // Range of supported values for random variable k. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. using boost::math::tools::max_value; return std::pair(static_cast(0), max_value()); } template inline RealType mean(const non_central_chi_squared_distribution& dist) { // Mean of poisson distribution = lambda. const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::mean()"; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy())) return r; return k + l; } // mean template inline RealType mode(const non_central_chi_squared_distribution& dist) { // mode. static const char* function = "mode(non_central_chi_squared_distribution<%1%> const&)"; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy())) return (RealType)r; return detail::generic_find_mode(dist, 1 + k, function); } template inline RealType variance(const non_central_chi_squared_distribution& dist) { // variance. const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::variance()"; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy())) return r; return 2 * (2 * l + k); } // RealType standard_deviation(const non_central_chi_squared_distribution& dist) // standard_deviation provided by derived accessors. template inline RealType skewness(const non_central_chi_squared_distribution& dist) { // skewness = sqrt(l). const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::skewness()"; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy())) return r; BOOST_MATH_STD_USING return pow(2 / (k + 2 * l), RealType(3)/2) * (k + 3 * l); } template inline RealType kurtosis_excess(const non_central_chi_squared_distribution& dist) { const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::kurtosis_excess()"; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy())) return r; return 12 * (k + 4 * l) / ((k + 2 * l) * (k + 2 * l)); } // kurtosis_excess template inline RealType kurtosis(const non_central_chi_squared_distribution& dist) { return kurtosis_excess(dist) + 3; } template inline RealType pdf(const non_central_chi_squared_distribution& dist, const RealType& x) { // Probability Density/Mass Function. return detail::nccs_pdf(dist, x); } // pdf template RealType cdf(const non_central_chi_squared_distribution& dist, const RealType& x) { const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)"; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy()) || !detail::check_positive_x( function, x, &r, Policy())) return r; return detail::non_central_chi_squared_cdf(x, k, l, false, Policy()); } // cdf template RealType cdf(const complemented2_type, RealType>& c) { // Complemented Cumulative Distribution Function const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)"; non_central_chi_squared_distribution const& dist = c.dist; RealType x = c.param; RealType k = dist.degrees_of_freedom(); RealType l = dist.non_centrality(); RealType r; if(!detail::check_df( function, k, &r, Policy()) || !detail::check_non_centrality( function, l, &r, Policy()) || !detail::check_positive_x( function, x, &r, Policy())) return r; return detail::non_central_chi_squared_cdf(x, k, l, true, Policy()); } // ccdf template inline RealType quantile(const non_central_chi_squared_distribution& dist, const RealType& p) { // Quantile (or Percent Point) function. return detail::nccs_quantile(dist, p, false); } // quantile template inline RealType quantile(const complemented2_type, RealType>& c) { // Quantile (or Percent Point) function. return detail::nccs_quantile(c.dist, c.param, true); } // quantile complement. } // namespace math } // namespace boost // This include must be at the end, *after* the accessors // for this distribution have been defined, in order to // keep compilers that support two-phase lookup happy. #include #endif // BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP