Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-11 08:14:59

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