class Hypergeometric {

  public double phypergeometric(int n, double p, int k, int r) {
    /*
     * Study group cannot be larger than population. If this happens there
     * is probably something wrong with the input data, but returning 1.0
     * prevents confusing and wrong output.
     */
    if (k >= n)
      return 1.0;

    if (r < 1) {
      return 1.0; // Not valid for r < 2, less than 2 study genes.
    }

    double q = 1.0 - p;
    int np = (int) Math.round(n * p); // Round to nearest int
    int nq = (int) Math.round(n * q);

    double log_n_choose_k = lNchooseK(n, k);
    int top = k;
    if (np < k) {
      top = np;
    }

    double lfoo = lNchooseK(np, top) + lNchooseK(nq, k - top);

    double sum = 0.0;

    for (int i = top; i >= r; --i) {
      sum += Math.exp(lfoo - log_n_choose_k);
      if (i > r) {
        lfoo = lfoo
          + Math.log((double) i / (double) (np - i + 1))
          + Math.log((double) (nq - k + i)
          / (double) (k - i + 1));
      }
    }
    return sum;
  }

  public  Hypergeometric();

}