summaryrefslogtreecommitdiff
path: root/boost/math/distributions/detail/inv_discrete_quantile.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/math/distributions/detail/inv_discrete_quantile.hpp')
-rw-r--r--boost/math/distributions/detail/inv_discrete_quantile.hpp172
1 files changed, 128 insertions, 44 deletions
diff --git a/boost/math/distributions/detail/inv_discrete_quantile.hpp b/boost/math/distributions/detail/inv_discrete_quantile.hpp
index 9397e7c7c2..23e00b8e03 100644
--- a/boost/math/distributions/detail/inv_discrete_quantile.hpp
+++ b/boost/math/distributions/detail/inv_discrete_quantile.hpp
@@ -19,8 +19,8 @@ struct distribution_quantile_finder
typedef typename Dist::value_type value_type;
typedef typename Dist::policy_type policy_type;
- distribution_quantile_finder(const Dist d, value_type p, value_type q)
- : dist(d), target(p < q ? p : q), comp(p < q ? false : true) {}
+ distribution_quantile_finder(const Dist d, value_type p, bool c)
+ : dist(d), target(p), comp(c) {}
value_type operator()(value_type const& x)
{
@@ -73,7 +73,7 @@ typename Dist::value_type
do_inverse_discrete_quantile(
const Dist& dist,
const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ bool comp,
typename Dist::value_type guess,
const typename Dist::value_type& multiplier,
typename Dist::value_type adder,
@@ -87,7 +87,7 @@ typename Dist::value_type
BOOST_MATH_STD_USING
- distribution_quantile_finder<Dist> f(dist, p, q);
+ distribution_quantile_finder<Dist> f(dist, p, comp);
//
// Max bounds of the distribution:
//
@@ -215,7 +215,7 @@ typename Dist::value_type
while(((boost::math::sign)(fb) == (boost::math::sign)(fa)) && (a != b))
{
if(count == 0)
- policies::raise_evaluation_error(function, "Unable to bracket root, last nearest value was %1%", b, policy_type());
+ return policies::raise_evaluation_error(function, "Unable to bracket root, last nearest value was %1%", b, policy_type());
a = b;
fa = fb;
b *= multiplier;
@@ -242,7 +242,7 @@ typename Dist::value_type
return 0;
}
if(count == 0)
- policies::raise_evaluation_error(function, "Unable to bracket root, last nearest value was %1%", a, policy_type());
+ return policies::raise_evaluation_error(function, "Unable to bracket root, last nearest value was %1%", a, policy_type());
b = a;
fb = fa;
a /= multiplier;
@@ -280,6 +280,80 @@ typename Dist::value_type
return (r.first + r.second) / 2;
}
//
+// Some special routine for rounding up and down:
+// We want to check and see if we are very close to an integer, and if so test to see if
+// that integer is an exact root of the cdf. We do this because our root finder only
+// guarantees to find *a root*, and there can sometimes be many consecutive floating
+// point values which are all roots. This is especially true if the target probability
+// is very close 1.
+//
+template <class Dist>
+inline typename Dist::value_type round_to_floor(const Dist& d, typename Dist::value_type result, typename Dist::value_type p, bool c)
+{
+ BOOST_MATH_STD_USING
+ typename Dist::value_type cc = ceil(result);
+ typename Dist::value_type pp = cc <= support(d).second ? c ? cdf(complement(d, cc)) : cdf(d, cc) : 1;
+ if(pp == p)
+ result = cc;
+ else
+ result = floor(result);
+ //
+ // Now find the smallest integer <= result for which we get an exact root:
+ //
+ while(result != 0)
+ {
+ cc = result - 1;
+ if(cc < support(d).first)
+ break;
+ pp = c ? cdf(complement(d, cc)) : cdf(d, cc);
+ if(pp == p)
+ result = cc;
+ else if(c ? pp > p : pp < p)
+ break;
+ result -= 1;
+ }
+
+ return result;
+}
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable:4127)
+#endif
+
+template <class Dist>
+inline typename Dist::value_type round_to_ceil(const Dist& d, typename Dist::value_type result, typename Dist::value_type p, bool c)
+{
+ BOOST_MATH_STD_USING
+ typename Dist::value_type cc = floor(result);
+ typename Dist::value_type pp = cc >= support(d).first ? c ? cdf(complement(d, cc)) : cdf(d, cc) : 0;
+ if(pp == p)
+ result = cc;
+ else
+ result = ceil(result);
+ //
+ // Now find the largest integer >= result for which we get an exact root:
+ //
+ while(true)
+ {
+ cc = result + 1;
+ if(cc > support(d).second)
+ break;
+ pp = c ? cdf(complement(d, cc)) : cdf(d, cc);
+ if(pp == p)
+ result = cc;
+ else if(c ? pp < p : pp > p)
+ break;
+ result += 1;
+ }
+
+ return result;
+}
+
+#ifdef BOOST_MSVC
+#pragma warning(pop)
+#endif
+//
// Now finally are the public API functions.
// There is one overload for each policy,
// each one is responsible for selecting the correct
@@ -290,20 +364,26 @@ template <class Dist>
inline typename Dist::value_type
inverse_discrete_quantile(
const Dist& dist,
- const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ typename Dist::value_type p,
+ bool c,
const typename Dist::value_type& guess,
const typename Dist::value_type& multiplier,
const typename Dist::value_type& adder,
const policies::discrete_quantile<policies::real>&,
boost::uintmax_t& max_iter)
{
- if(p <= pdf(dist, 0))
+ if(p > 0.5)
+ {
+ p = 1 - p;
+ c = !c;
+ }
+ typename Dist::value_type pp = c ? 1 - p : p;
+ if(pp <= pdf(dist, 0))
return 0;
return do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
guess,
multiplier,
adder,
@@ -316,7 +396,7 @@ inline typename Dist::value_type
inverse_discrete_quantile(
const Dist& dist,
const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ bool c,
const typename Dist::value_type& guess,
const typename Dist::value_type& multiplier,
const typename Dist::value_type& adder,
@@ -325,32 +405,33 @@ inline typename Dist::value_type
{
typedef typename Dist::value_type value_type;
BOOST_MATH_STD_USING
- if(p <= pdf(dist, 0))
+ typename Dist::value_type pp = c ? 1 - p : p;
+ if(pp <= pdf(dist, 0))
return 0;
//
// What happens next depends on whether we're looking for an
// upper or lower quantile:
//
- if(p < 0.5f)
- return floor(do_inverse_discrete_quantile(
+ if(pp < 0.5f)
+ return round_to_floor(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
(guess < 1 ? value_type(1) : (value_type)floor(guess)),
multiplier,
adder,
tools::equal_floor(),
- max_iter));
+ max_iter), p, c);
// else:
- return ceil(do_inverse_discrete_quantile(
+ return round_to_ceil(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
(value_type)ceil(guess),
multiplier,
adder,
tools::equal_ceil(),
- max_iter));
+ max_iter), p, c);
}
template <class Dist>
@@ -358,7 +439,7 @@ inline typename Dist::value_type
inverse_discrete_quantile(
const Dist& dist,
const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ bool c,
const typename Dist::value_type& guess,
const typename Dist::value_type& multiplier,
const typename Dist::value_type& adder,
@@ -367,32 +448,33 @@ inline typename Dist::value_type
{
typedef typename Dist::value_type value_type;
BOOST_MATH_STD_USING
- if(p <= pdf(dist, 0))
+ typename Dist::value_type pp = c ? 1 - p : p;
+ if(pp <= pdf(dist, 0))
return 0;
//
// What happens next depends on whether we're looking for an
// upper or lower quantile:
//
- if(p < 0.5f)
- return ceil(do_inverse_discrete_quantile(
+ if(pp < 0.5f)
+ return round_to_ceil(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
ceil(guess),
multiplier,
adder,
tools::equal_ceil(),
- max_iter));
+ max_iter), p, c);
// else:
- return floor(do_inverse_discrete_quantile(
+ return round_to_floor(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
(guess < 1 ? value_type(1) : floor(guess)),
multiplier,
adder,
tools::equal_floor(),
- max_iter));
+ max_iter), p, c);
}
template <class Dist>
@@ -400,7 +482,7 @@ inline typename Dist::value_type
inverse_discrete_quantile(
const Dist& dist,
const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ bool c,
const typename Dist::value_type& guess,
const typename Dist::value_type& multiplier,
const typename Dist::value_type& adder,
@@ -409,17 +491,18 @@ inline typename Dist::value_type
{
typedef typename Dist::value_type value_type;
BOOST_MATH_STD_USING
- if(p <= pdf(dist, 0))
+ typename Dist::value_type pp = c ? 1 - p : p;
+ if(pp <= pdf(dist, 0))
return 0;
- return floor(do_inverse_discrete_quantile(
+ return round_to_floor(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
(guess < 1 ? value_type(1) : floor(guess)),
multiplier,
adder,
tools::equal_floor(),
- max_iter));
+ max_iter), p, c);
}
template <class Dist>
@@ -427,7 +510,7 @@ inline typename Dist::value_type
inverse_discrete_quantile(
const Dist& dist,
const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ bool c,
const typename Dist::value_type& guess,
const typename Dist::value_type& multiplier,
const typename Dist::value_type& adder,
@@ -435,17 +518,18 @@ inline typename Dist::value_type
boost::uintmax_t& max_iter)
{
BOOST_MATH_STD_USING
- if(p <= pdf(dist, 0))
+ typename Dist::value_type pp = c ? 1 - p : p;
+ if(pp <= pdf(dist, 0))
return 0;
- return ceil(do_inverse_discrete_quantile(
+ return round_to_ceil(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
ceil(guess),
multiplier,
adder,
tools::equal_ceil(),
- max_iter));
+ max_iter), p, c);
}
template <class Dist>
@@ -453,7 +537,7 @@ inline typename Dist::value_type
inverse_discrete_quantile(
const Dist& dist,
const typename Dist::value_type& p,
- const typename Dist::value_type& q,
+ bool c,
const typename Dist::value_type& guess,
const typename Dist::value_type& multiplier,
const typename Dist::value_type& adder,
@@ -462,26 +546,26 @@ inline typename Dist::value_type
{
typedef typename Dist::value_type value_type;
BOOST_MATH_STD_USING
- if(p <= pdf(dist, 0))
+ typename Dist::value_type pp = c ? 1 - p : p;
+ if(pp <= pdf(dist, 0))
return 0;
//
// Note that we adjust the guess to the nearest half-integer:
// this increase the chances that we will bracket the root
// with two results that both round to the same integer quickly.
//
- return floor(do_inverse_discrete_quantile(
+ return round_to_floor(dist, do_inverse_discrete_quantile(
dist,
p,
- q,
+ c,
(guess < 0.5f ? value_type(1.5f) : floor(guess + 0.5f) + 0.5f),
multiplier,
adder,
tools::equal_nearest_integer(),
- max_iter) + 0.5f);
+ max_iter) + 0.5f, p, c);
}
}}} // namespaces
#endif // BOOST_MATH_DISTRIBUTIONS_DETAIL_INV_DISCRETE_QUANTILE
-