Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-11-01 08:22:07

0001 // boost\math\distributions\non_central_chi_squared.hpp
0002 
0003 // Copyright John Maddock 2008.
0004 // Copyright Matt Borland 2024.
0005 // Use, modification and distribution are subject to the
0006 // Boost Software License, Version 1.0.
0007 // (See accompanying file LICENSE_1_0.txt
0008 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 
0010 #ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP
0011 #define BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP
0012 
0013 #include <boost/math/tools/config.hpp>
0014 #include <boost/math/tools/tuple.hpp>
0015 #include <boost/math/tools/cstdint.hpp>
0016 #include <boost/math/tools/numeric_limits.hpp>
0017 #include <boost/math/distributions/fwd.hpp>
0018 #include <boost/math/special_functions/gamma.hpp> // for incomplete gamma. gamma_q
0019 #include <boost/math/special_functions/bessel.hpp> // for cyl_bessel_i
0020 #include <boost/math/special_functions/round.hpp> // for llround
0021 #include <boost/math/distributions/complement.hpp> // complements
0022 #include <boost/math/distributions/chi_squared.hpp> // central distribution
0023 #include <boost/math/distributions/detail/common_error_handling.hpp> // error checks
0024 #include <boost/math/special_functions/fpclassify.hpp> // isnan.
0025 #include <boost/math/tools/roots.hpp> // for root finding.
0026 #include <boost/math/distributions/detail/generic_mode.hpp>
0027 #include <boost/math/distributions/detail/generic_quantile.hpp>
0028 #include <boost/math/policies/policy.hpp>
0029 
0030 namespace boost
0031 {
0032    namespace math
0033    {
0034 
0035       template <class RealType, class Policy>
0036       class non_central_chi_squared_distribution;
0037 
0038       namespace detail{
0039 
0040          template <class T, class Policy>
0041          BOOST_MATH_GPU_ENABLED T non_central_chi_square_q(T x, T f, T theta, const Policy& pol, T init_sum = 0)
0042          {
0043             //
0044             // Computes the complement of the Non-Central Chi-Square
0045             // Distribution CDF by summing a weighted sum of complements
0046             // of the central-distributions.  The weighting factor is
0047             // a Poisson Distribution.
0048             //
0049             // This is an application of the technique described in:
0050             //
0051             // Computing discrete mixtures of continuous
0052             // distributions: noncentral chisquare, noncentral t
0053             // and the distribution of the square of the sample
0054             // multiple correlation coefficient.
0055             // D. Benton, K. Krishnamoorthy.
0056             // Computational Statistics & Data Analysis 43 (2003) 249 - 267
0057             //
0058             BOOST_MATH_STD_USING
0059 
0060             // Special case:
0061             if(x == 0)
0062                return 1;
0063 
0064             //
0065             // Initialize the variables we'll be using:
0066             //
0067             T lambda = theta / 2;
0068             T del = f / 2;
0069             T y = x / 2;
0070             boost::math::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
0071             T errtol = boost::math::policies::get_epsilon<T, Policy>();
0072             T sum = init_sum;
0073             //
0074             // k is the starting location for iteration, we'll
0075             // move both forwards and backwards from this point.
0076             // k is chosen as the peek of the Poisson weights, which
0077             // will occur *before* the largest term.
0078             //
0079             long long k = llround(lambda, pol);
0080             // Forwards and backwards Poisson weights:
0081             T poisf = boost::math::gamma_p_derivative(static_cast<T>(1 + k), lambda, pol);
0082             T poisb = poisf * k / lambda;
0083             // Initial forwards central chi squared term:
0084             T gamf = boost::math::gamma_q(del + k, y, pol);
0085             // Forwards and backwards recursion terms on the central chi squared:
0086             T xtermf = boost::math::gamma_p_derivative(del + 1 + k, y, pol);
0087             T xtermb = xtermf * (del + k) / y;
0088             // Initial backwards central chi squared term:
0089             T gamb = gamf - xtermb;
0090 
0091             //
0092             // Forwards iteration first, this is the
0093             // stable direction for the gamma function
0094             // recurrences:
0095             //
0096             long long i;
0097             for(i = k; static_cast<boost::math::uintmax_t>(i-k) < max_iter; ++i)
0098             {
0099                T term = poisf * gamf;
0100                sum += term;
0101                poisf *= lambda / (i + 1);
0102                gamf += xtermf;
0103                xtermf *= y / (del + i + 1);
0104                if(((sum == 0) || (fabs(term / sum) < errtol)) && (term >= poisf * gamf))
0105                   break;
0106             }
0107             //Error check:
0108             if(static_cast<boost::math::uintmax_t>(i-k) >= max_iter)
0109                return policies::raise_evaluation_error("cdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); // LCOV_EXCL_LINE
0110             //
0111             // Now backwards iteration: the gamma
0112             // function recurrences are unstable in this
0113             // direction, we rely on the terms diminishing in size
0114             // faster than we introduce cancellation errors.
0115             // For this reason it's very important that we start
0116             // *before* the largest term so that backwards iteration
0117             // is strictly converging.
0118             //
0119             for(i = k - 1; i >= 0; --i)
0120             {
0121                T term = poisb * gamb;
0122                sum += term;
0123                poisb *= i / lambda;
0124                xtermb *= (del + i) / y;
0125                gamb -= xtermb;
0126                if((sum == 0) || (fabs(term / sum) < errtol))
0127                   break;
0128             }
0129 
0130             return sum;
0131          }
0132 
0133          template <class T, class Policy>
0134          BOOST_MATH_GPU_ENABLED T non_central_chi_square_p_ding(T x, T f, T theta, const Policy& pol, T init_sum = 0)
0135          {
0136             //
0137             // This is an implementation of:
0138             //
0139             // Algorithm AS 275:
0140             // Computing the Non-Central #2 Distribution Function
0141             // Cherng G. Ding
0142             // Applied Statistics, Vol. 41, No. 2. (1992), pp. 478-482.
0143             //
0144             // This uses a stable forward iteration to sum the
0145             // CDF, unfortunately this can not be used for large
0146             // values of the non-centrality parameter because:
0147             // * The first term may underflow to zero.
0148             // * We may need an extra-ordinary number of terms
0149             //   before we reach the first *significant* term.
0150             //
0151             BOOST_MATH_STD_USING
0152             // Special case:
0153             if(x == 0)
0154                return 0;
0155             T tk = boost::math::gamma_p_derivative(f/2 + 1, x/2, pol);
0156             T lambda = theta / 2;
0157             T vk = exp(-lambda);
0158             T uk = vk;
0159             T sum = init_sum + tk * vk;
0160             if(sum == 0)
0161                return sum;
0162 
0163             boost::math::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
0164             T errtol = boost::math::policies::get_epsilon<T, Policy>();
0165 
0166             int i;
0167             T lterm(0), term(0);
0168             for(i = 1; static_cast<boost::math::uintmax_t>(i) < max_iter; ++i)
0169             {
0170                tk = tk * x / (f + 2 * i);
0171                uk = uk * lambda / i;
0172                vk = vk + uk;
0173                lterm = term;
0174                term = vk * tk;
0175                sum += term;
0176                if((fabs(term / sum) < errtol) && (term <= lterm))
0177                   break;
0178             }
0179             //Error check:
0180             if(static_cast<boost::math::uintmax_t>(i) >= max_iter)
0181                return policies::raise_evaluation_error("cdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); // LCOV_EXCL_LINE
0182             return sum;
0183          }
0184 
0185 
0186          template <class T, class Policy>
0187          BOOST_MATH_GPU_ENABLED T non_central_chi_square_p(T y, T n, T lambda, const Policy& pol, T init_sum)
0188          {
0189             //
0190             // This is taken more or less directly from:
0191             //
0192             // Computing discrete mixtures of continuous
0193             // distributions: noncentral chisquare, noncentral t
0194             // and the distribution of the square of the sample
0195             // multiple correlation coefficient.
0196             // D. Benton, K. Krishnamoorthy.
0197             // Computational Statistics & Data Analysis 43 (2003) 249 - 267
0198             //
0199             // We're summing a Poisson weighting term multiplied by
0200             // a central chi squared distribution.
0201             //
0202             BOOST_MATH_STD_USING
0203             // Special case:
0204             if(y == 0)
0205                return 0;
0206             boost::math::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
0207             T errtol = boost::math::policies::get_epsilon<T, Policy>();
0208             T errorf(0), errorb(0);
0209 
0210             T x = y / 2;
0211             T del = lambda / 2;
0212             //
0213             // Starting location for the iteration, we'll iterate
0214             // both forwards and backwards from this point.  The
0215             // location chosen is the maximum of the Poisson weight
0216             // function, which ocurrs *after* the largest term in the
0217             // sum.
0218             //
0219             long long k = llround(del, pol);
0220             T a = n / 2 + k;
0221             // Central chi squared term for forward iteration:
0222             T gamkf = boost::math::gamma_p(a, x, pol);
0223 
0224             if(lambda == 0)
0225                return gamkf;
0226             // Central chi squared term for backward iteration:
0227             T gamkb = gamkf;
0228             // Forwards Poisson weight:
0229             T poiskf = gamma_p_derivative(static_cast<T>(k+1), del, pol);
0230             // Backwards Poisson weight:
0231             T poiskb = poiskf;
0232             // Forwards gamma function recursion term:
0233             T xtermf = boost::math::gamma_p_derivative(a, x, pol);
0234             // Backwards gamma function recursion term:
0235             T xtermb = xtermf * x / a;
0236             T sum = init_sum + poiskf * gamkf;
0237             if(sum == 0)
0238                return sum;
0239             int i = 1;
0240             //
0241             // Backwards recursion first, this is the stable
0242             // direction for gamma function recurrences:
0243             //
0244             while(i <= k)
0245             {
0246                xtermb *= (a - i + 1) / x;
0247                gamkb += xtermb;
0248                poiskb = poiskb * (k - i + 1) / del;
0249                errorf = errorb;
0250                errorb = gamkb * poiskb;
0251                sum += errorb;
0252                if((fabs(errorb / sum) < errtol) && (errorb <= errorf))
0253                   break;
0254                ++i;
0255             }
0256             i = 1;
0257             //
0258             // Now forwards recursion, the gamma function
0259             // recurrence relation is unstable in this direction,
0260             // so we rely on the magnitude of successive terms
0261             // decreasing faster than we introduce cancellation error.
0262             // For this reason it's vital that k is chosen to be *after*
0263             // the largest term, so that successive forward iterations
0264             // are strictly (and rapidly) converging.
0265             //
0266             do
0267             {
0268                xtermf = xtermf * x / (a + i - 1);
0269                gamkf = gamkf - xtermf;
0270                poiskf = poiskf * del / (k + i);
0271                errorf = poiskf * gamkf;
0272                sum += errorf;
0273                ++i;
0274             }while((fabs(errorf / sum) > errtol) && (static_cast<boost::math::uintmax_t>(i) < max_iter));
0275 
0276             //Error check:
0277             if(static_cast<boost::math::uintmax_t>(i) >= max_iter)
0278                return policies::raise_evaluation_error("cdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); // LCOV_EXCL_LINE
0279 
0280             return sum;
0281          }
0282 
0283          template <class T, class Policy>
0284          BOOST_MATH_GPU_ENABLED T non_central_chi_square_pdf(T x, T n, T lambda, const Policy& pol)
0285          {
0286             //
0287             // As above but for the PDF:
0288             //
0289             BOOST_MATH_STD_USING
0290             boost::math::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
0291             T errtol = boost::math::policies::get_epsilon<T, Policy>();
0292             T x2 = x / 2;
0293             T n2 = n / 2;
0294             T l2 = lambda / 2;
0295             T sum = 0;
0296             long long k = lltrunc(l2);
0297             T pois = gamma_p_derivative(static_cast<T>(k + 1), l2, pol) * gamma_p_derivative(static_cast<T>(n2 + k), x2);
0298             if(pois == 0)
0299                return 0;
0300             T poisb = pois;
0301             for(long long i = k; ; ++i)
0302             {
0303                sum += pois;
0304                if(pois / sum < errtol)
0305                   break;
0306                if(static_cast<boost::math::uintmax_t>(i - k) >= max_iter)
0307                   return policies::raise_evaluation_error("pdf(non_central_chi_squared_distribution<%1%>, %1%)", "Series did not converge, closest value was %1%", sum, pol); // LCOV_EXCL_LINE
0308                pois *= l2 * x2 / ((i + 1) * (n2 + i));
0309             }
0310             for(long long i = k - 1; i >= 0; --i)
0311             {
0312                poisb *= (i + 1) * (n2 + i) / (l2 * x2);
0313                sum += poisb;
0314                if(poisb / sum < errtol)
0315                   break;
0316             }
0317             return sum / 2;
0318          }
0319 
0320          template <class RealType, class Policy>
0321          BOOST_MATH_GPU_ENABLED inline RealType non_central_chi_squared_cdf(RealType x, RealType k, RealType l, bool invert, const Policy&)
0322          {
0323             typedef typename policies::evaluation<RealType, Policy>::type value_type;
0324             typedef typename policies::normalise<
0325                Policy,
0326                policies::promote_float<false>,
0327                policies::promote_double<false>,
0328                policies::discrete_quantile<>,
0329                policies::assert_undefined<> >::type forwarding_policy;
0330 
0331             BOOST_MATH_STD_USING
0332             value_type result;
0333             if(l == 0)
0334               return invert == false ? cdf(boost::math::chi_squared_distribution<RealType, Policy>(k), x) : cdf(complement(boost::math::chi_squared_distribution<RealType, Policy>(k), x));
0335             else if(x > k + l)
0336             {
0337                // Complement is the smaller of the two:
0338                result = detail::non_central_chi_square_q(
0339                   static_cast<value_type>(x),
0340                   static_cast<value_type>(k),
0341                   static_cast<value_type>(l),
0342                   forwarding_policy(),
0343                   static_cast<value_type>(invert ? 0 : -1));
0344                invert = !invert;
0345             }
0346             else if(l < 200)
0347             {
0348                // For small values of the non-centrality parameter
0349                // we can use Ding's method:
0350                result = detail::non_central_chi_square_p_ding(
0351                   static_cast<value_type>(x),
0352                   static_cast<value_type>(k),
0353                   static_cast<value_type>(l),
0354                   forwarding_policy(),
0355                   static_cast<value_type>(invert ? -1 : 0));
0356             }
0357             else
0358             {
0359                // For largers values of the non-centrality
0360                // parameter Ding's method will consume an
0361                // extra-ordinary number of terms, and worse
0362                // may return zero when the result is in fact
0363                // finite, use Krishnamoorthy's method instead:
0364                result = detail::non_central_chi_square_p(
0365                   static_cast<value_type>(x),
0366                   static_cast<value_type>(k),
0367                   static_cast<value_type>(l),
0368                   forwarding_policy(),
0369                   static_cast<value_type>(invert ? -1 : 0));
0370             }
0371             if(invert)
0372                result = -result;
0373             return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0374                result,
0375                "boost::math::non_central_chi_squared_cdf<%1%>(%1%, %1%, %1%)");
0376          }
0377 
0378          template <class T, class Policy>
0379          struct nccs_quantile_functor
0380          {
0381             BOOST_MATH_GPU_ENABLED nccs_quantile_functor(const non_central_chi_squared_distribution<T,Policy>& d, T t, bool c)
0382                : dist(d), target(t), comp(c) {}
0383 
0384             BOOST_MATH_GPU_ENABLED T operator()(const T& x)
0385             {
0386                return comp ?
0387                   target - cdf(complement(dist, x))
0388                   : cdf(dist, x) - target;
0389             }
0390 
0391          private:
0392             non_central_chi_squared_distribution<T,Policy> dist;
0393             T target;
0394             bool comp;
0395          };
0396 
0397          template <class RealType, class Policy>
0398          BOOST_MATH_GPU_ENABLED RealType nccs_quantile(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& p, bool comp)
0399          {
0400             BOOST_MATH_STD_USING
0401             constexpr auto function = "quantile(non_central_chi_squared_distribution<%1%>, %1%)";
0402             typedef typename policies::evaluation<RealType, Policy>::type value_type;
0403             typedef typename policies::normalise<
0404                Policy,
0405                policies::promote_float<false>,
0406                policies::promote_double<false>,
0407                policies::discrete_quantile<>,
0408                policies::assert_undefined<> >::type forwarding_policy;
0409 
0410             value_type k = dist.degrees_of_freedom();
0411             value_type l = dist.non_centrality();
0412             value_type r;
0413             if(!detail::check_df(
0414                function,
0415                k, &r, Policy())
0416                ||
0417             !detail::check_non_centrality(
0418                function,
0419                l,
0420                &r,
0421                Policy())
0422                ||
0423             !detail::check_probability(
0424                function,
0425                static_cast<value_type>(p),
0426                &r,
0427                Policy()))
0428                   return static_cast<RealType>(r);
0429             //
0430             // Special cases get short-circuited first:
0431             //
0432             if(p == 0)
0433                return comp ? policies::raise_overflow_error<RealType>(function, 0, Policy()) : 0;
0434             if(p == 1)
0435                return comp ? 0 : policies::raise_overflow_error<RealType>(function, 0, Policy());
0436             //
0437             // This is Pearson's approximation to the quantile, see
0438             // Pearson, E. S. (1959) "Note on an approximation to the distribution of
0439             // noncentral chi squared", Biometrika 46: 364.
0440             // See also:
0441             // "A comparison of approximations to percentiles of the noncentral chi2-distribution",
0442             // Hardeo Sahai and Mario Miguel Ojeda, Revista de Matematica: Teoria y Aplicaciones 2003 10(1-2) : 57-76.
0443             // Note that the latter reference refers to an approximation of the CDF, when they really mean the quantile.
0444             //
0445             value_type b = -(l * l) / (k + 3 * l);
0446             value_type c = (k + 3 * l) / (k + 2 * l);
0447             value_type ff = (k + 2 * l) / (c * c);
0448             value_type guess;
0449             if(comp)
0450             {
0451                guess = b + c * quantile(complement(chi_squared_distribution<value_type, forwarding_policy>(ff), p));
0452             }
0453             else
0454             {
0455                guess = b + c * quantile(chi_squared_distribution<value_type, forwarding_policy>(ff), p);
0456             }
0457             //
0458             // Sometimes guess goes very small or negative, in that case we have
0459             // to do something else for the initial guess, this approximation
0460             // was provided in a private communication from Thomas Luu, PhD candidate,
0461             // University College London.  It's an asymptotic expansion for the
0462             // quantile which usually gets us within an order of magnitude of the
0463             // correct answer.
0464             // Fast and accurate parallel computation of quantile functions for random number generation,
0465             // Thomas LuuDoctorial Thesis 2016
0466             // http://discovery.ucl.ac.uk/1482128/
0467             //
0468             if(guess < 0.005)
0469             {
0470                value_type pp = comp ? 1 - p : p;
0471                //guess = pow(pow(value_type(2), (k / 2 - 1)) * exp(l / 2) * pp * k, 2 / k);
0472                guess = pow(pow(value_type(2), (k / 2 - 1)) * exp(l / 2) * pp * k * boost::math::tgamma(k / 2, forwarding_policy()), (2 / k));
0473                if(guess == 0)
0474                   guess = tools::min_value<value_type>();
0475             }
0476             value_type result = detail::generic_quantile(
0477                non_central_chi_squared_distribution<value_type, forwarding_policy>(k, l),
0478                p,
0479                guess,
0480                comp,
0481                function);
0482 
0483             return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0484                result,
0485                function);
0486          }
0487 
0488          template <class RealType, class Policy>
0489          BOOST_MATH_GPU_ENABLED RealType nccs_pdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x)
0490          {
0491             BOOST_MATH_STD_USING
0492             constexpr auto function = "pdf(non_central_chi_squared_distribution<%1%>, %1%)";
0493             typedef typename policies::evaluation<RealType, Policy>::type value_type;
0494             typedef typename policies::normalise<
0495                Policy,
0496                policies::promote_float<false>,
0497                policies::promote_double<false>,
0498                policies::discrete_quantile<>,
0499                policies::assert_undefined<> >::type forwarding_policy;
0500 
0501             value_type k = dist.degrees_of_freedom();
0502             value_type l = dist.non_centrality();
0503             value_type r;
0504             if(!detail::check_df(
0505                function,
0506                k, &r, Policy())
0507                ||
0508             !detail::check_non_centrality(
0509                function,
0510                l,
0511                &r,
0512                Policy())
0513                ||
0514             !detail::check_positive_x(
0515                function,
0516                (value_type)x,
0517                &r,
0518                Policy()))
0519                   return static_cast<RealType>(r);
0520 
0521          if(l == 0)
0522             return pdf(boost::math::chi_squared_distribution<RealType, forwarding_policy>(dist.degrees_of_freedom()), x);
0523 
0524          // Special case:
0525          if(x == 0)
0526             return 0;
0527          if(l > 50)
0528          {
0529             r = non_central_chi_square_pdf(static_cast<value_type>(x), k, l, forwarding_policy());
0530          }
0531          else
0532          {
0533             r = log(x / l) * (k / 4 - 0.5f) - (x + l) / 2;
0534             if(fabs(r) >= tools::log_max_value<RealType>() / 4)
0535             {
0536                r = non_central_chi_square_pdf(static_cast<value_type>(x), k, l, forwarding_policy());
0537             }
0538             else
0539             {
0540                r = exp(r);
0541                r = 0.5f * r
0542                   * boost::math::cyl_bessel_i(k/2 - 1, sqrt(l * x), forwarding_policy());
0543             }
0544          }
0545          return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0546                r,
0547                function);
0548          }
0549 
0550          template <class RealType, class Policy>
0551          struct degrees_of_freedom_finder
0552          {
0553             BOOST_MATH_GPU_ENABLED degrees_of_freedom_finder(
0554                RealType lam_, RealType x_, RealType p_, bool c)
0555                : lam(lam_), x(x_), p(p_), comp(c) {}
0556 
0557             BOOST_MATH_GPU_ENABLED RealType operator()(const RealType& v)
0558             {
0559                non_central_chi_squared_distribution<RealType, Policy> d(v, lam);
0560                return comp ?
0561                   RealType(p - cdf(complement(d, x)))
0562                   : RealType(cdf(d, x) - p);
0563             }
0564          private:
0565             RealType lam;
0566             RealType x;
0567             RealType p;
0568             bool comp;
0569          };
0570 
0571          template <class RealType, class Policy>
0572          BOOST_MATH_GPU_ENABLED inline RealType find_degrees_of_freedom(
0573             RealType lam, RealType x, RealType p, RealType q, const Policy& pol)
0574          {
0575             constexpr auto function = "non_central_chi_squared<%1%>::find_degrees_of_freedom";
0576             if((p == 0) || (q == 0))
0577             {
0578                //
0579                // Can't a thing if one of p and q is zero:
0580                //
0581                return policies::raise_evaluation_error<RealType>(function, "Can't find degrees of freedom when the probability is 0 or 1, only possible answer is %1%", // LCOV_EXCL_LINE
0582                   RealType(boost::math::numeric_limits<RealType>::quiet_NaN()), Policy()); // LCOV_EXCL_LINE
0583             }
0584             degrees_of_freedom_finder<RealType, Policy> f(lam, x, p < q ? p : q, p < q ? false : true);
0585             tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
0586             boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0587             //
0588             // Pick an initial guess that we know will give us a probability
0589             // right around 0.5.
0590             //
0591             RealType guess = x - lam;
0592             if(guess < 1)
0593                guess = 1;
0594             boost::math::pair<RealType, RealType> ir = tools::bracket_and_solve_root(
0595                f, guess, RealType(2), false, tol, max_iter, pol);
0596             RealType result = ir.first + (ir.second - ir.first) / 2;
0597             if(max_iter >= policies::get_max_root_iterations<Policy>())
0598             {
0599                return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" // LCOV_EXCL_LINE
0600                   " or there is no answer to problem.  Current best guess is %1%", result, Policy()); // LCOV_EXCL_LINE
0601             }
0602             return result;
0603          }
0604 
0605          template <class RealType, class Policy>
0606          struct non_centrality_finder
0607          {
0608             BOOST_MATH_GPU_ENABLED non_centrality_finder(
0609                RealType v_, RealType x_, RealType p_, bool c)
0610                : v(v_), x(x_), p(p_), comp(c) {}
0611 
0612             BOOST_MATH_GPU_ENABLED RealType operator()(const RealType& lam)
0613             {
0614                non_central_chi_squared_distribution<RealType, Policy> d(v, lam);
0615                return comp ?
0616                   RealType(p - cdf(complement(d, x)))
0617                   : RealType(cdf(d, x) - p);
0618             }
0619          private:
0620             RealType v;
0621             RealType x;
0622             RealType p;
0623             bool comp;
0624          };
0625 
0626          template <class RealType, class Policy>
0627          BOOST_MATH_GPU_ENABLED inline RealType find_non_centrality(
0628             RealType v, RealType x, RealType p, RealType q, const Policy& pol)
0629          {
0630             constexpr auto function = "non_central_chi_squared<%1%>::find_non_centrality";
0631             if((p == 0) || (q == 0))
0632             {
0633                //
0634                // Can't do a thing if one of p and q is zero:
0635                //
0636                return policies::raise_evaluation_error<RealType>(function, "Can't find non centrality parameter when the probability is 0 or 1, only possible answer is %1%", // LCOV_EXCL_LINE
0637                   RealType(boost::math::numeric_limits<RealType>::quiet_NaN()), Policy()); // LCOV_EXCL_LINE
0638             }
0639             non_centrality_finder<RealType, Policy> f(v, x, p < q ? p : q, p < q ? false : true);
0640             tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
0641             boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0642             //
0643             // Pick an initial guess that we know will give us a probability
0644             // right around 0.5.
0645             //
0646             RealType guess = x - v;
0647             if(guess < 1)
0648                guess = 1;
0649             boost::math::pair<RealType, RealType> ir = tools::bracket_and_solve_root(
0650                f, guess, RealType(2), false, tol, max_iter, pol);
0651             RealType result = ir.first + (ir.second - ir.first) / 2;
0652             if(max_iter >= policies::get_max_root_iterations<Policy>())
0653             {
0654                return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" // LCOV_EXCL_LINE
0655                   " or there is no answer to problem.  Current best guess is %1%", result, Policy()); // LCOV_EXCL_LINE
0656             }
0657             return result;
0658          }
0659 
0660       }
0661 
0662       template <class RealType = double, class Policy = policies::policy<> >
0663       class non_central_chi_squared_distribution
0664       {
0665       public:
0666          typedef RealType value_type;
0667          typedef Policy policy_type;
0668 
0669          BOOST_MATH_GPU_ENABLED non_central_chi_squared_distribution(RealType df_, RealType lambda) : df(df_), ncp(lambda)
0670          {
0671             constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::non_central_chi_squared_distribution(%1%,%1%)";
0672             RealType r;
0673             detail::check_df(
0674                function,
0675                df, &r, Policy());
0676             detail::check_non_centrality(
0677                function,
0678                ncp,
0679                &r,
0680                Policy());
0681          } // non_central_chi_squared_distribution constructor.
0682 
0683          BOOST_MATH_GPU_ENABLED RealType degrees_of_freedom() const
0684          { // Private data getter function.
0685             return df;
0686          }
0687          BOOST_MATH_GPU_ENABLED RealType non_centrality() const
0688          { // Private data getter function.
0689             return ncp;
0690          }
0691          BOOST_MATH_GPU_ENABLED static RealType find_degrees_of_freedom(RealType lam, RealType x, RealType p)
0692          {
0693             constexpr auto function = "non_central_chi_squared<%1%>::find_degrees_of_freedom";
0694             typedef typename policies::evaluation<RealType, Policy>::type eval_type;
0695             typedef typename policies::normalise<
0696                Policy,
0697                policies::promote_float<false>,
0698                policies::promote_double<false>,
0699                policies::discrete_quantile<>,
0700                policies::assert_undefined<> >::type forwarding_policy;
0701             eval_type result = detail::find_degrees_of_freedom(
0702                static_cast<eval_type>(lam),
0703                static_cast<eval_type>(x),
0704                static_cast<eval_type>(p),
0705                static_cast<eval_type>(1-p),
0706                forwarding_policy());
0707             return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0708                result,
0709                function);
0710          }
0711          template <class A, class B, class C>
0712          BOOST_MATH_GPU_ENABLED static RealType find_degrees_of_freedom(const complemented3_type<A,B,C>& c)
0713          {
0714             constexpr auto function = "non_central_chi_squared<%1%>::find_degrees_of_freedom";
0715             typedef typename policies::evaluation<RealType, Policy>::type eval_type;
0716             typedef typename policies::normalise<
0717                Policy,
0718                policies::promote_float<false>,
0719                policies::promote_double<false>,
0720                policies::discrete_quantile<>,
0721                policies::assert_undefined<> >::type forwarding_policy;
0722             eval_type result = detail::find_degrees_of_freedom(
0723                static_cast<eval_type>(c.dist),
0724                static_cast<eval_type>(c.param1),
0725                static_cast<eval_type>(1-c.param2),
0726                static_cast<eval_type>(c.param2),
0727                forwarding_policy());
0728             return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0729                result,
0730                function);
0731          }
0732          BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(RealType v, RealType x, RealType p)
0733          {
0734             constexpr auto function = "non_central_chi_squared<%1%>::find_non_centrality";
0735             typedef typename policies::evaluation<RealType, Policy>::type eval_type;
0736             typedef typename policies::normalise<
0737                Policy,
0738                policies::promote_float<false>,
0739                policies::promote_double<false>,
0740                policies::discrete_quantile<>,
0741                policies::assert_undefined<> >::type forwarding_policy;
0742             eval_type result = detail::find_non_centrality(
0743                static_cast<eval_type>(v),
0744                static_cast<eval_type>(x),
0745                static_cast<eval_type>(p),
0746                static_cast<eval_type>(1-p),
0747                forwarding_policy());
0748             return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0749                result,
0750                function);
0751          }
0752          template <class A, class B, class C>
0753          BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const complemented3_type<A,B,C>& c)
0754          {
0755             constexpr auto function = "non_central_chi_squared<%1%>::find_non_centrality";
0756             typedef typename policies::evaluation<RealType, Policy>::type eval_type;
0757             typedef typename policies::normalise<
0758                Policy,
0759                policies::promote_float<false>,
0760                policies::promote_double<false>,
0761                policies::discrete_quantile<>,
0762                policies::assert_undefined<> >::type forwarding_policy;
0763             eval_type result = detail::find_non_centrality(
0764                static_cast<eval_type>(c.dist),
0765                static_cast<eval_type>(c.param1),
0766                static_cast<eval_type>(1-c.param2),
0767                static_cast<eval_type>(c.param2),
0768                forwarding_policy());
0769             return policies::checked_narrowing_cast<RealType, forwarding_policy>(
0770                result,
0771                function);
0772          }
0773       private:
0774          // Data member, initialized by constructor.
0775          RealType df; // degrees of freedom.
0776          RealType ncp; // non-centrality parameter
0777       }; // template <class RealType, class Policy> class non_central_chi_squared_distribution
0778 
0779       typedef non_central_chi_squared_distribution<double> non_central_chi_squared; // Reserved name of type double.
0780 
0781       #ifdef __cpp_deduction_guides
0782       template <class RealType>
0783       non_central_chi_squared_distribution(RealType,RealType)->non_central_chi_squared_distribution<typename boost::math::tools::promote_args<RealType>::type>;
0784       #endif
0785 
0786       // Non-member functions to give properties of the distribution.
0787 
0788       template <class RealType, class Policy>
0789       BOOST_MATH_GPU_ENABLED inline const boost::math::pair<RealType, RealType> range(const non_central_chi_squared_distribution<RealType, Policy>& /* dist */)
0790       { // Range of permissible values for random variable k.
0791          using boost::math::tools::max_value;
0792          return boost::math::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // Max integer?
0793       }
0794 
0795       template <class RealType, class Policy>
0796       BOOST_MATH_GPU_ENABLED inline const boost::math::pair<RealType, RealType> support(const non_central_chi_squared_distribution<RealType, Policy>& /* dist */)
0797       { // Range of supported values for random variable k.
0798          // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
0799          using boost::math::tools::max_value;
0800          return boost::math::pair<RealType, RealType>(static_cast<RealType>(0),  max_value<RealType>());
0801       }
0802 
0803       template <class RealType, class Policy>
0804       BOOST_MATH_GPU_ENABLED inline RealType mean(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0805       { // Mean of poisson distribution = lambda.
0806          constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::mean()";
0807          RealType k = dist.degrees_of_freedom();
0808          RealType l = dist.non_centrality();
0809          RealType r;
0810          if(!detail::check_df(
0811             function,
0812             k, &r, Policy())
0813             ||
0814          !detail::check_non_centrality(
0815             function,
0816             l,
0817             &r,
0818             Policy()))
0819                return static_cast<RealType>(r);
0820          return k + l;
0821       } // mean
0822 
0823       template <class RealType, class Policy>
0824       BOOST_MATH_GPU_ENABLED inline RealType mode(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0825       { // mode.
0826          constexpr auto function = "mode(non_central_chi_squared_distribution<%1%> const&)";
0827 
0828          RealType k = dist.degrees_of_freedom();
0829          RealType l = dist.non_centrality();
0830          RealType r;
0831          if(!detail::check_df(
0832             function,
0833             k, &r, Policy())
0834             ||
0835          !detail::check_non_centrality(
0836             function,
0837             l,
0838             &r,
0839             Policy()))
0840                return static_cast<RealType>(r);
0841          bool asymptotic_mode = k < l/4;
0842          RealType starting_point = asymptotic_mode ? k + l - RealType(3) : RealType(1) + k;
0843          return detail::generic_find_mode(dist, starting_point, function);
0844       }
0845 
0846       template <class RealType, class Policy>
0847       BOOST_MATH_GPU_ENABLED inline RealType variance(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0848       { // variance.
0849          constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::variance()";
0850          RealType k = dist.degrees_of_freedom();
0851          RealType l = dist.non_centrality();
0852          RealType r;
0853          if(!detail::check_df(
0854             function,
0855             k, &r, Policy())
0856             ||
0857          !detail::check_non_centrality(
0858             function,
0859             l,
0860             &r,
0861             Policy()))
0862                return static_cast<RealType>(r);
0863          return 2 * (2 * l + k);
0864       }
0865 
0866       // RealType standard_deviation(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0867       // standard_deviation provided by derived accessors.
0868 
0869       template <class RealType, class Policy>
0870       BOOST_MATH_GPU_ENABLED inline RealType skewness(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0871       { // skewness = sqrt(l).
0872          constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::skewness()";
0873          RealType k = dist.degrees_of_freedom();
0874          RealType l = dist.non_centrality();
0875          RealType r;
0876          if(!detail::check_df(
0877             function,
0878             k, &r, Policy())
0879             ||
0880          !detail::check_non_centrality(
0881             function,
0882             l,
0883             &r,
0884             Policy()))
0885                return static_cast<RealType>(r);
0886          BOOST_MATH_STD_USING
0887             return pow(2 / (k + 2 * l), RealType(3)/2) * (k + 3 * l);
0888       }
0889 
0890       template <class RealType, class Policy>
0891       BOOST_MATH_GPU_ENABLED inline RealType kurtosis_excess(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0892       {
0893          constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::kurtosis_excess()";
0894          RealType k = dist.degrees_of_freedom();
0895          RealType l = dist.non_centrality();
0896          RealType r;
0897          if(!detail::check_df(
0898             function,
0899             k, &r, Policy())
0900             ||
0901          !detail::check_non_centrality(
0902             function,
0903             l,
0904             &r,
0905             Policy()))
0906                return static_cast<RealType>(r);
0907          return 12 * (k + 4 * l) / ((k + 2 * l) * (k + 2 * l));
0908       } // kurtosis_excess
0909 
0910       template <class RealType, class Policy>
0911       BOOST_MATH_GPU_ENABLED inline RealType kurtosis(const non_central_chi_squared_distribution<RealType, Policy>& dist)
0912       {
0913          return kurtosis_excess(dist) + 3;
0914       }
0915 
0916       template <class RealType, class Policy>
0917       BOOST_MATH_GPU_ENABLED inline RealType pdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x)
0918       { // Probability Density/Mass Function.
0919          return detail::nccs_pdf(dist, x);
0920       } // pdf
0921 
0922       template <class RealType, class Policy>
0923       BOOST_MATH_GPU_ENABLED RealType cdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x)
0924       {
0925          constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)";
0926          RealType k = dist.degrees_of_freedom();
0927          RealType l = dist.non_centrality();
0928          RealType r;
0929          if(!detail::check_df(
0930             function,
0931             k, &r, Policy())
0932             ||
0933          !detail::check_non_centrality(
0934             function,
0935             l,
0936             &r,
0937             Policy())
0938             ||
0939          !detail::check_positive_x(
0940             function,
0941             x,
0942             &r,
0943             Policy()))
0944                return static_cast<RealType>(r);
0945 
0946          return detail::non_central_chi_squared_cdf(x, k, l, false, Policy());
0947       } // cdf
0948 
0949       template <class RealType, class Policy>
0950       BOOST_MATH_GPU_ENABLED RealType cdf(const complemented2_type<non_central_chi_squared_distribution<RealType, Policy>, RealType>& c)
0951       { // Complemented Cumulative Distribution Function
0952          constexpr auto function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)";
0953          non_central_chi_squared_distribution<RealType, Policy> const& dist = c.dist;
0954          RealType x = c.param;
0955          RealType k = dist.degrees_of_freedom();
0956          RealType l = dist.non_centrality();
0957          RealType r;
0958          if(!detail::check_df(
0959             function,
0960             k, &r, Policy())
0961             ||
0962          !detail::check_non_centrality(
0963             function,
0964             l,
0965             &r,
0966             Policy())
0967             ||
0968          !detail::check_positive_x(
0969             function,
0970             x,
0971             &r,
0972             Policy()))
0973                return static_cast<RealType>(r);
0974 
0975          return detail::non_central_chi_squared_cdf(x, k, l, true, Policy());
0976       } // ccdf
0977 
0978       template <class RealType, class Policy>
0979       BOOST_MATH_GPU_ENABLED inline RealType quantile(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& p)
0980       { // Quantile (or Percent Point) function.
0981          return detail::nccs_quantile(dist, p, false);
0982       } // quantile
0983 
0984       template <class RealType, class Policy>
0985       BOOST_MATH_GPU_ENABLED inline RealType quantile(const complemented2_type<non_central_chi_squared_distribution<RealType, Policy>, RealType>& c)
0986       { // Quantile (or Percent Point) function.
0987          return detail::nccs_quantile(c.dist, c.param, true);
0988       } // quantile complement.
0989 
0990    } // namespace math
0991 } // namespace boost
0992 
0993 // This include must be at the end, *after* the accessors
0994 // for this distribution have been defined, in order to
0995 // keep compilers that support two-phase lookup happy.
0996 #include <boost/math/distributions/detail/derived_accessors.hpp>
0997 
0998 #endif // BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP