33 static constexpr bool log =
false;
71 static double lfactorial(
int x) {
77 case 0:
case 1:
return 0;
78 case 2:
return std::log(2.0);
79 case 3:
return std::log(6.0);
80 case 4:
return std::log(24.0);
81 case 5:
return std::log(120.0);
82 case 6:
return std::log(720.0);
83 case 7:
return std::log(5040.0);
84 case 8:
return std::log(40320.0);
85 case 9:
return std::log(362880.0);
86 case 10:
return std::log(3628800.0);
87 case 11:
return std::log(39916800.0);
88 case 12:
return std::log(479001600.0);
94 return 1.0/6.0 * std::log(y * (1 + 4 * y * (1 + 2 * y)) + 1.0/30.0) + y * std::log(y) - y + 0.5 * std::log(3.14159265358979323846);
108 static long double compute_cumulative(
int drawn_inside,
int num_inside,
int num_outside,
int num_drawn) {
110 long double probability = 1;
114 long double cumulative = 0;
116 for (
int k = drawn_inside; k > 0 && probability > 0; --k) {
117 probability *=
static_cast<double>(k) *
static_cast<double>(num_outside - num_drawn + k) /
static_cast<double>(num_inside - k + 1) /
static_cast<double>(num_drawn - k + 1);
118 cumulative += probability;
124 static double compute_probability_mass(
int drawn_inside,
int num_inside,
int num_outside,
int num_drawn) {
125 int num_total = num_inside + num_outside;
126 return lfactorial(num_inside) - lfactorial(drawn_inside) - lfactorial(num_inside - drawn_inside)
127 + lfactorial(num_outside) - lfactorial(num_drawn - drawn_inside) - lfactorial(num_outside - num_drawn + drawn_inside)
128 - lfactorial(num_total) + lfactorial(num_drawn) + lfactorial(num_total - num_drawn);
132 static double invert_tail_log(
double val) {
137 if (val > -std::log(2)) {
138 auto p = -std::expm1(val);
139 return (p > 0 ? std::log(p) : -std::numeric_limits<double>::infinity());
141 auto p = -std::exp(val);
142 return (p > -1 ? std::log1p(p) : -std::numeric_limits<double>::infinity());
146 double compute_tail_probability(
long double cumulative,
double scale,
bool do_lower)
const {
148 double lp = std::log1p(cumulative) + scale;
152 return invert_tail_log(lp);
155 auto p = (1 + cumulative) * std::exp(scale);
156 return (do_lower ? p : 1 - p);
160 double edge_handler(
bool zero)
const {
162 return (log ? -std::numeric_limits<double>::infinity() : 0);
164 return (log ? 0 : 1);
169 double core(
int drawn_inside,
int num_inside,
int num_outside,
int num_drawn)
const {
175 if (drawn_inside <= 0 || drawn_inside < num_drawn - num_outside) {
176 return edge_handler(!upper_tail);
177 }
else if (drawn_inside >= num_drawn || drawn_inside >= num_inside) {
178 return edge_handler(upper_tail);
185 bool do_lower = !upper_tail;
186 if (drawn_inside * (num_inside + num_outside) > num_drawn * num_inside) {
187 auto tmp = num_inside;
188 num_inside = num_outside;
190 drawn_inside = num_drawn - drawn_inside - 1;
191 do_lower = !do_lower;
194 double logscale = compute_probability_mass(drawn_inside, num_inside, num_outside, num_drawn);
195 auto cum = compute_cumulative(drawn_inside, num_inside, num_outside, num_drawn);
196 return compute_tail_probability(cum, logscale, do_lower);
209 double run(
int drawn_inside,
int num_inside,
int num_outside,
int num_drawn)
const {
210 return core(drawn_inside, num_inside, num_outside, num_drawn);
double run(int drawn_inside, int num_inside, int num_outside, int num_drawn) const
Definition HypergeometricTail.hpp:209