Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/nmath/rhyper.c
Line
Count
Source
1
/*
2
 *  Mathlib : A C Library of Special Functions
3
 *  Copyright (C) 2000-2026 The R Core Team
4
 *  Copyright (C) 2005-2020 The R Foundation
5
 *  Copyright (C) 1998 Ross Ihaka
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, a copy is available at
19
 *  https://www.R-project.org/Licenses/
20
 *
21
 *  SYNOPSIS
22
 *
23
 *    #include <Rmath.h>
24
 *    double rhyper(double NR, double NB, double n);
25
 *
26
 *  DESCRIPTION
27
 *
28
 *    Random variates from the hypergeometric distribution.
29
 *    Returns the number of white balls drawn when kk balls
30
 *    are drawn at random from an urn containing nn1 white
31
 *    and nn2 black balls.
32
 *
33
 *  REFERENCE
34
 *
35
 *    V. Kachitvichyanukul and B. Schmeiser (1985).
36
 *    ``Computer generation of hypergeometric random variates,''
37
 *    Journal of Statistical Computation and Simulation 22, 127-145.
38
 *
39
 *    The original algorithm had a bug -- R bug report PR#7314 --
40
 *    giving numbers slightly too small in case III h2pe
41
 *    where (m < 100 || ix <= 50) , see below.
42
 */
43
44
#include "nmath.h"
45
#include "dpq.h"
46
#include <limits.h>
47
48
// afc(i) :=  ln( i! )  [logarithm of the factorial i] = {R:} lgamma(i + 1) = {C:} lgammafn(i + 1)
49
static double afc(int i)
50
0
{
51
    // If (i > 7), use Stirling's approximation, otherwise use table lookup.
52
0
    const static double al[8] =
53
0
    {
54
0
  0.0,/*ln(0!)=ln(1)*/
55
0
  0.0,/*ln(1!)=ln(1)*/
56
0
  0.69314718055994530941723212145817,/*ln(2) */
57
0
  1.79175946922805500081247735838070,/*ln(6) */
58
0
  3.17805383034794561964694160129705,/*ln(24)*/
59
0
  4.78749174278204599424770093452324,
60
0
  6.57925121201010099506017829290394,
61
0
  8.52516136106541430016553103634712
62
  /* 10.60460290274525022841722740072165, approx. value below =
63
     10.6046028788027; rel.error = 2.26 10^{-9}
64
65
    FIXME: Use constants and if(n > ..) decisions from ./stirlerr.c
66
    -----  will be even *faster* for n > 500 (or so)
67
  */
68
0
    };
69
70
0
    if (i < 0) {
71
0
  MATHLIB_WARNING(("rhyper.c: afc(i), i=%d < 0 -- SHOULD NOT HAPPEN!\n"), i);
72
0
  return -1; // unreached
73
0
    }
74
0
    if (i <= 7)
75
0
  return al[i];
76
    // else i >= 8 :
77
0
    double di = i, i2 = di*di;
78
0
    return (di + 0.5) * log(di) - di + M_LN_SQRT_2PI +
79
0
  (0.0833333333333333 - 0.00277777777777778 / i2) / di;
80
0
}
81
82
//     rhyper(NR, NB, n) -- NR 'red', NB 'blue', n drawn, how many are 'red'
83
double rhyper(double nn1in, double nn2in, double kkin)
84
0
{
85
    /* check parameter validity */
86
87
0
    if(!R_FINITE(nn1in) || !R_FINITE(nn2in) || !R_FINITE(kkin))
88
0
  ML_WARN_return_NAN;
89
90
0
    nn1in = R_forceint(nn1in);
91
0
    nn2in = R_forceint(nn2in);
92
0
    kkin  = R_forceint(kkin);
93
0
    double N = nn1in + nn2in;
94
95
0
    if (nn1in < 0 || nn2in < 0 || kkin < 0 || kkin > nn1in + nn2in)
96
0
  ML_WARN_return_NAN;
97
0
    if (N > INT_MAX || kkin >= INT_MAX) {
98
  /* large n -- evade integer overflow (and inappropriate algorithms)
99
     -------- */
100
#ifdef DEBUG_rhyper
101
  REprintf("rhyper(nn1=%.0f, nn2=%.0f, kk=%.0f): 'large n' case\n", nn1in, nn2in, kkin);
102
#endif
103
        // FIXME: Much faster to give rbinom() approx when appropriate; -> see Kuensch(1989)
104
  // Johnson, Kotz,.. p.258 (top) mention the *four* different binomial approximations
105
0
  if(kkin == 1.) { // Bernoulli
106
0
      return rbinom(kkin, nn1in / (nn1in + nn2in));
107
0
  }
108
  // Slow, but safe: return  F^{-1}(U)  where F(.) = phyper(.) and  U ~ U[0,1]
109
0
  return qhyper(unif_rand(), nn1in, nn2in, kkin,
110
0
          /*lower_tail =*/ false, /*log_p = */ false);
111
  // lower_tail=false: a thinko, is still "correct" as equiv. to  U <--> 1-U
112
0
    }
113
114
0
    int
115
0
  nn1 = (int)nn1in,
116
0
  nn2 = (int)nn2in,
117
0
  kk  = (int)kkin;
118
119
    /* These should become 'thread_local globals' : */
120
0
    static int ks = -1, n1s = -1, n2s = -1;
121
0
    static int m, minjx, maxjx;
122
0
    static int k, n1, n2; // <- not allowing larger integer par
123
124
0
    bool setup1, setup2;
125
    /* if new parameter values, initialize */
126
0
    if (nn1 != n1s || nn2 != n2s) { // n1 | n2 is changed: setup all
127
0
  setup1 = true;  setup2 = true;
128
0
    } else if (kk != ks) { // n1 & n2 are unchanged: setup 'k' only
129
0
  setup1 = false; setup2 = true;
130
0
    } else { // all three unchanged ==> no setup
131
0
  setup1 = false; setup2 = false;
132
0
    }
133
0
    if (setup1) { // n1 & n2
134
0
  n1s = nn1; n2s = nn2; // save
135
0
  if (nn1 <= nn2) {
136
0
      n1 = nn1; n2 = nn2;
137
0
  } else { // nn2 < nn1
138
0
      n1 = nn2; n2 = nn1;
139
0
  }
140
  // now have n1 <= n2
141
0
    }
142
0
    if (setup2) { // k
143
0
  ks = kk; // save
144
0
  if ((double)kk + kk >= N) { // this could overflow
145
0
      k = (int)(N - kk);
146
0
  } else {
147
0
      k = kk;
148
0
  }
149
  // now have  k < N/2 = (n1+n2)/2
150
0
    }
151
0
    if (setup1 || setup2) {
152
0
  m = (int) ((k + 1.) * (n1 + 1.) / (N + 2.)); // m := floor(adjusted mean E[.])
153
0
  minjx = imax2(0, k - n2);
154
0
  maxjx = imin2(n1, k);
155
#ifdef DEBUG_rhyper
156
  REprintf("rhyper(n1=%d, n2=%d, k=%d), setup: floor(a.mean)=: m = %d, [min,maxjx]= [%d,%d]\n",
157
     nn1, nn2, kk, m, minjx, maxjx);
158
#endif
159
0
    }
160
#ifdef DEBUG_rhyper
161
    else
162
  REprintf("rhyper(n1=%d, n2=%d, k=%d); setup UNchanged\n", nn1, nn2, kk);
163
#endif
164
165
    // generate random variate --- Three basic cases ---------------------------
166
167
0
    int ix; // return value (coerced to double at the very end) .. FIXME?? what if overflows
168
169
0
    if (minjx == maxjx) { /* I: degenerate distribution ---------------- */
170
#ifdef DEBUG_rhyper
171
  REprintf("rhyper(), branch I (degenerate): ix := maxjx = %d\n", maxjx);
172
#endif
173
0
  ix = maxjx;
174
0
  goto L_finis; // return appropriate variate
175
176
0
    } else if (m - minjx < 10) { // II: (Scaled) algorithm HIN (inverse transformation) ----
177
0
  const static double scale = 1e25; // scaling factor against (early) underflow
178
0
  const static double con = 57.5646273248511421;
179
            // 25*log(10) = log(scale) { <==> exp(con) == scale }
180
0
  static double w,
181
0
      lw; // = log(w);  w = exp(lw) * scale = exp(lw + log(scale)) = exp(lw + con)
182
0
  if (setup1 || setup2) {
183
      // NB:  n1 <= n2  here
184
0
      if (k < n2) {
185
0
    lw = afc(n2) + afc(n1 + n2 - k) - afc(n2 - k) - afc(n1 + n2);
186
0
      } else {
187
0
    lw = afc(n1) + afc(     k     ) - afc(k - n2) - afc(n1 + n2);
188
0
      }
189
0
      w = exp(lw + con);
190
0
  }
191
192
#ifdef DEBUG_rhyper
193
  REprintf("  branch II; w = %g %s\n", w,
194
     (w > 0) ? "> 0" : "= 0 <==> Underflow __NOT GOOD__");
195
#endif
196
0
  if(w <= 0.)
197
0
      MATHLIB_ERROR(_("w = %g <= 0: Underflow in rhyper()  SHOULD NOT HAPPEN!"), w);
198
  // FIXME: MM has code "rhyper.c+M3" for switching to log space if w == 0
199
200
0
  double p, u;
201
0
      L10:
202
0
  p = w;
203
0
  ix = minjx;
204
0
  u = unif_rand() * scale;
205
#ifdef DEBUG_rhyper
206
  REprintf("  _new_ u = %g\n", u);
207
#endif
208
0
  while (u > p) {
209
0
      u -= p;
210
0
      p *= ((double) n1 - ix) * (k - ix);
211
0
      ix++;
212
0
      p = p / ix / (n2 - k + ix);
213
#ifdef DEBUG_rhyper
214
      REprintf("       ix=%3d, u=%11g, p=%20.14g (u-p=%g)\n", ix, u, p, u-p);
215
#endif
216
0
      if (ix > maxjx)
217
0
    goto L10;
218
      // FIXME  if(p == 0.)  we also "have lost"  => goto L10
219
0
  }
220
221
0
    } else { /* III : H2PE Algorithm --------------------------------------- */
222
223
0
  static double a, xl, xr, lamdl, lamdr, p1, p2, p3;
224
0
  if (setup1 || setup2) {
225
0
      double
226
0
    s = sqrt((N - k) * k * n1 * n2 / (N - 1) / N / N),
227
228
    /* remark: d is defined in reference without int. */
229
    /* the truncation centers the cell boundaries at 0.5 */
230
0
    d = (int) (1.5 * s) + .5;
231
0
      xl = m - d + .5;
232
0
      xr = m + d + .5;
233
0
      a = afc(m) + afc(n1 - m) + afc(k - m) + afc(n2 - k + m);
234
0
      double
235
0
    kl = exp(a - afc((int) (xl)) - afc((int) (n1 - xl))
236
0
       - afc((int) (k - xl))
237
0
       - afc((int) (n2 - k + xl))),
238
0
    kr = exp(a - afc((int) (xr - 1))
239
0
       - afc((int) (n1 - xr + 1))
240
0
       - afc((int) (k - xr + 1))
241
0
       - afc((int) (n2 - k + xr - 1)));
242
0
      lamdl = -log(xl * (n2 - k + xl) / (n1 - xl + 1) / (k - xl + 1));
243
0
      lamdr = -log((n1 - xr + 1) * (k - xr + 1) / xr / (n2 - k + xr));
244
0
      p1 = d + d;
245
0
      p2 = p1 + kl / lamdl;
246
0
      p3 = p2 + kr / lamdr;
247
#ifdef DEBUG_rhyper
248
      REprintf("rhyper(), branch III {accept/reject}: (xl,xr)= (%g,%g); lamdl,r = (%g,%g)\n",
249
         xl, xr, lamdl, lamdr);
250
      REprintf(" --------> (p1,p2,p3) = (%g,%g,%g)\n", p1,p2,p3);
251
  }
252
  else
253
      REprintf("rhyper(), branch III {accept/reject}: NO setup needed; lamdl,r = (%g,%g)\n",
254
         lamdl, lamdr);
255
#else
256
0
        }
257
0
#endif
258
259
  /* acceptance/rejection test */
260
0
  bool reject = true;
261
0
        int n_uv = 0;
262
0
    do { // L30: -------------- acceptance / rejection : loop while reject=true -------------
263
0
  double
264
0
      u = unif_rand() * p3,
265
0
      v = unif_rand();
266
0
  n_uv++;
267
0
  if(n_uv >= 10000) {
268
0
      REprintf("rhyper(*, n1=%d, n2=%d, k=%d): branch III: giving up after %d rejections\n",
269
0
         nn1, nn2, kk, n_uv);
270
0
      ML_WARN_return_NAN;
271
0
        }
272
#ifdef DEBUG_rhyper
273
  REprintf(" ... L30 [%d]: new (u=%g, v ~ U[0,1]=%g): ", n_uv, u,v);
274
#endif
275
276
0
  if (u < p1) {   /* rectangular region */
277
0
      ix = (int) (xl + u);
278
#ifdef DEBUG_rhyper
279
      REprintf(" rectangular: ix=%d\n", ix);
280
#endif
281
0
  } else if (u <= p2) { /* left tail */
282
0
      ix = (int) (xl + log(v) / lamdl);
283
#ifdef DEBUG_rhyper
284
      REprintf(" left tail: ix=%d %s", ix, (ix < minjx)? "< minjx (=> L30)\n" : ">= minjx;");
285
#endif
286
0
      if (ix < minjx)
287
0
    continue; // goto L30
288
0
      v = v * (u - p1) * lamdl;
289
0
  } else {   /* right tail */
290
0
      ix = (int) (xr - log(v) / lamdr);
291
#ifdef DEBUG_rhyper
292
      REprintf(" right tail: ix=%d %s", ix, (ix > maxjx)? "> maxjx (=> L30)\n" : "< maxjx;");
293
#endif
294
0
      if (ix > maxjx)
295
0
    continue; // goto L30
296
0
      v = v * (u - p2) * lamdr;
297
0
  }
298
#ifdef DEBUG_rhyper
299
  if(u >= p1) REprintf(" new v = %g\n", v);
300
#endif
301
302
0
  if (m < 100 || ix <= 50) {
303
      /* explicit evaluation */
304
      /* The original algorithm (and TOMS 668) have
305
       f = f * i * (n2 - k + i) / (n1 - i) / (k - i);
306
         in the (m > ix) case, but the definition of the
307
         recurrence relation on p134 shows that the +1 is
308
         needed. */
309
0
      double f = 1.0;
310
0
      if (m < ix) {
311
0
    for (int i = m + 1; i <= ix; i++)
312
0
        f = f * (n1 - i + 1) * (k - i + 1) / (n2 - k + i) / i;
313
0
      } else if (m > ix) {
314
0
    for (int i = ix + 1; i <= m; i++)
315
0
        f = f * i * (n2 - k + i) / (n1 - i + 1) / (k - i + 1);
316
0
      }
317
#ifdef DEBUG_rhyper
318
      REprintf(" small m or ix: f = %g\n", f);
319
#endif
320
0
      if (v <= f) {
321
0
    reject = false;
322
0
      }
323
0
  } else {
324
325
0
      const static double deltal = 0.0078;
326
0
      const static double deltau = 0.0034;
327
328
#ifdef DEBUG_rhyper
329
      REprintf(" ... accept/reject 'large' case v=%g\n", v);
330
#endif
331
      /* squeeze using upper and lower bounds */
332
0
      double
333
0
    y = ix,
334
0
    y1 = y + 1.0,
335
0
    ym = y - m,
336
0
    yn = n1 - y + 1.0,
337
0
    yk = k  - y + 1.0,
338
0
    nk = n2 - k + y1,
339
0
    r = -ym / y1,
340
0
    s =  ym / yn,
341
0
    t =  ym / yk,
342
0
    e = -ym / nk,
343
0
    g = yn * yk / (y1 * nk) - 1.0,
344
0
    dg = (g < 0.0) ? 1. + g : 1.,
345
0
    gu = g * (1.0 + g * (-0.5 + g / 3.0)),
346
0
    gl = gu - .25 * (g * g * g * g) / dg,
347
0
    xm = m + 0.5,
348
0
    xn = n1 - m + 0.5,
349
0
    xk = k - m + 0.5,
350
0
    nm = n2 - k + xm,
351
0
    ub = y * gu - m * gl + deltau
352
0
    + xm * r * (1. + r * (-0.5 + r / 3.0))
353
0
    + xn * s * (1. + s * (-0.5 + s / 3.0))
354
0
    + xk * t * (1. + t * (-0.5 + t / 3.0))
355
0
    + nm * e * (1. + e * (-0.5 + e / 3.0));
356
      /* test against upper bound */
357
0
      double alv = log(v);
358
0
      if (alv > ub) {
359
0
    reject = true;
360
0
      } else {
361
    /* test against lower bound */
362
0
    double dr = xm * (r * r * r * r);
363
0
    if (r < 0.)
364
0
        dr /= (1.0 + r);
365
0
    double ds = xn * (s * s * s * s);
366
0
    if (s < 0.0)
367
0
        ds /= (1.0 + s);
368
0
    double dt = xk * (t * t * t * t);
369
0
    if (t < 0.0)
370
0
        dt /= (1.0 + t);
371
0
    double de = nm * (e * e * e * e);
372
0
    if (e < 0.0)
373
0
        de /= (1.0 + e);
374
0
    if (alv < ub - 0.25 * (dr + ds + dt + de)
375
0
        + (y + m) * (gl - gu) - deltal) {
376
0
        reject = false;
377
0
    }
378
0
    else {
379
        /* * Stirling's formula to machine accuracy
380
         */
381
0
        if (alv <= (a - afc(ix) - afc(n1 - ix)
382
0
        - afc(k - ix) - afc(n2 - k + ix))) {
383
0
      reject = false;
384
0
        } else {
385
0
      reject = true;
386
0
        }
387
0
    }
388
0
      }
389
0
  } // end{ III "large" }
390
391
0
    } while (reject);
392
393
0
    } // end{branch III}
394
395
396
0
L_finis:  /* return appropriate variate */
397
398
#ifdef DEBUG_rhyper
399
    REprintf(" L_finis: ix = %d, then", ix);
400
#endif
401
0
    if ((double)kk + kk >= N) {
402
0
  if (nn1 > nn2) {
403
0
      ix = kk - nn2 + ix;
404
0
  } else {
405
0
      ix = nn1 - ix;
406
0
  }
407
0
    } else if (nn1 > nn2) {
408
0
  ix = kk - ix;
409
0
    }
410
#ifdef DEBUG_rhyper
411
    REprintf(" %d\n", ix);
412
#endif
413
0
    return ix;
414
0
}