/** Computes the probability <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>. */
  public static double prob(double n, double p, int x) {
    final int SLIM = 15; // To avoid overflow
    final double MAXEXP = (Num.DBL_MAX_EXP - 1) * Num.LN2; // To avoid overflow
    final double MINEXP = (Num.DBL_MIN_EXP - 1) * Num.LN2; // To avoid underflow
    double y;

    if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]");
    if (n <= 0.0) throw new IllegalArgumentException("n <= 0.0");
    if (x < 0) return 0.0;
    if (p >= 1.0) { // In fact, p == 1
      if (x == 0) return 1.0;
      else return 0.0;
    }
    if (p <= 0.0) // In fact, p == 0
    return 0.0;

    y =
        Num.lnGamma(n + x)
            - (Num.lnFactorial(x) + Num.lnGamma(n))
            + n * Math.log(p)
            + x * Math.log1p(-p);

    if (y >= MAXEXP) throw new IllegalArgumentException("term overflow");
    return Math.exp(y);
  }
    public double evaluate(double gam) {
      if (gam <= 0) return 1.0e100;

      double sum = 0.0;
      for (int j = 0; j < m; j++) sum += Num.digamma(gam + x[j]);
      return sum / m + Math.log(p) - Num.digamma(gam);
    }
    public double evaluate(double s) {
      if (s <= 0) return 1.0e100;
      double sum = 0.0;
      double p = s / (s + mean);

      for (int j = 0; j < max; j++) sum += Fj[j] / (s + (double) j);

      return sum + m * Math.log(p);
    }
  /** Computes the distribution function. */
  public static double cdf(double n, double p, int x) {
    final double EPSILON = DiscreteDistributionInt.EPSILON;
    final int LIM1 = 100000;
    double sum, term, termmode;
    int i, mode;
    final double q = 1.0 - p;

    if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]");
    if (n <= 0.0) throw new IllegalArgumentException("n <= 0.0");

    if (x < 0) return 0.0;
    if (p >= 1.0) // In fact, p == 1
    return 1.0;
    if (p <= 0.0) // In fact, p == 0
    return 0.0;

    // Compute the maximum term
    mode = 1 + (int) Math.floor((n * q - 1.0) / p);
    if (mode < 0) mode = 0;
    else if (mode > x) mode = x;

    if (mode <= LIM1) {
      sum = term = termmode = prob(n, p, mode);
      for (i = mode; i > 0; i--) {
        term *= i / (q * (n + i - 1.0));
        if (term < EPSILON) break;
        sum += term;
      }

      term = termmode;
      for (i = mode; i < x; i++) {
        term *= q * (n + i) / (i + 1);
        if (term < EPSILON) break;
        sum += term;
      }
      if (sum <= 1.0) return sum;
      else return 1.0;
    } else
      // return 1.0 - BinomialDist.cdf (x + n, p, n - 1);
      return BetaDist.cdf(n, x + 1.0, 15, p);
  }
  /**
   * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of
   * this object.
   */
  public void setParams(double n, double p) {
    /* *
     *  Compute all probability terms of the negative binomial distribution;
     *  start at the mode, and calculate probabilities on each side until they
     *  become smaller than EPSILON. Set all others to 0.
     */
    supportA = 0;
    int i, mode, Nmax;
    int imin, imax;
    double sum;
    double[] P; // Negative Binomial mass probabilities
    double[] F; // Negative Binomial cumulative

    if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]");
    if (n <= 0.0) throw new IllegalArgumentException("n <= 0");

    this.n = n;
    this.p = p;

    // Compute the mode (at the maximum term)
    mode = 1 + (int) Math.floor((n * (1.0 - p) - 1.0) / p);

    /* *
     For mode > MAXN, we shall not use pre-computed arrays.
     mode < 0 should be impossible, unless overflow of long occur, in
     which case mode will be = LONG_MIN.
    */

    if (mode < 0.0 || mode > MAXN) {
      pdf = null;
      cdf = null;
      return;
    }

    /* *
      In theory, the negative binomial distribution has an infinite range.
      But for i > Nmax, probabilities should be extremely small.
      Nmax = Mean + 16 * Standard deviation.
    */

    Nmax = (int) (n * (1.0 - p) / p + 16 * Math.sqrt(n * (1.0 - p) / (p * p)));
    if (Nmax < 32) Nmax = 32;
    P = new double[1 + Nmax];

    double epsilon = EPSILON / prob(n, p, mode);

    // We shall normalize by explicitly summing all terms >= epsilon
    sum = P[mode] = 1.0;

    // Start from the maximum and compute terms > epsilon on each side.
    i = mode;
    while (i > 0 && P[i] >= epsilon) {
      P[i - 1] = P[i] * i / ((1.0 - p) * (n + i - 1));
      i--;
      sum += P[i];
    }
    imin = i;

    i = mode;
    while (P[i] >= epsilon) {
      P[i + 1] = P[i] * (1.0 - p) * (n + i) / (i + 1);
      i++;
      sum += P[i];
      if (i == Nmax - 1) {
        Nmax *= 2;
        double[] nT = new double[1 + Nmax];
        System.arraycopy(P, 0, nT, 0, P.length);
        P = nT;
      }
    }
    imax = i;

    // Renormalize the sum of probabilities to 1
    for (i = imin; i <= imax; i++) P[i] /= sum;

    // Compute the cumulative probabilities for F and keep them in the
    // lower part of CDF.
    F = new double[1 + Nmax];
    F[imin] = P[imin];
    i = imin;
    while (i < imax && F[i] < 0.5) {
      i++;
      F[i] = F[i - 1] + P[i];
    }

    // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in
    // the array CDF
    xmed = i;

    // Compute the cumulative probabilities of the complementary
    // distribution 1 - F and keep them in the upper part of the array
    F[imax] = P[imax];
    i = imax - 1;
    do {
      F[i] = P[i] + F[i + 1];
      i--;
    } while (i > xmed);

    xmin = imin;
    xmax = imax;
    pdf = new double[imax + 1 - imin];
    cdf = new double[imax + 1 - imin];
    System.arraycopy(P, imin, pdf, 0, imax + 1 - imin);
    System.arraycopy(F, imin, cdf, 0, imax + 1 - imin);
  }
 /**
  * Computes and returns the standard deviation of the negative binomial distribution with
  * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
  *
  * @return the standard deviation of the negative binomial distribution
  */
 public static double getStandardDeviation(double n, double p) {
   return Math.sqrt(NegativeBinomialDist.getVariance(n, p));
 }
  /** Computes the inverse function without precomputing tables. */
  public static int inverseF(double n, double p, double u) {
    if (u < 0.0 || u > 1.0) throw new IllegalArgumentException("u is not in [0,1]");
    if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]");
    if (n <= 0.0) throw new IllegalArgumentException("n <= 0");
    if (p >= 1.0) // In fact, p == 1
    return 0;
    if (p <= 0.0) // In fact, p == 0
    return 0;
    if (u <= prob(n, p, 0)) return 0;
    if (u >= 1.0) return Integer.MAX_VALUE;

    double sum, term, termmode;
    final double q = 1.0 - p;

    // Compute the maximum term
    int mode = 1 + (int) Math.floor((n * q - 1.0) / p);
    if (mode < 0) mode = 0;
    int i = mode;
    term = prob(n, p, i);
    while ((term >= u) && (term > Double.MIN_NORMAL)) {
      i /= 2;
      term = prob(n, p, i);
    }

    if (term <= Double.MIN_NORMAL) {
      i *= 2;
      term = prob(n, p, i);
      while (term >= u && (term > Double.MIN_NORMAL)) {
        term *= i / (q * (n + i - 1.0));
        i--;
      }
    }

    mode = i;
    sum = termmode = prob(n, p, i);

    for (i = mode; i > 0; i--) {
      term *= i / (q * (n + i - 1.0));
      if (term < EPSILON) break;
      sum += term;
    }

    term = termmode;
    i = mode;
    double prev = -1;
    if (sum < u) {
      // The CDF at the mode is less than u, so we add term to get >= u.
      while ((sum < u) && (sum > prev)) {
        term *= q * (n + i) / (i + 1);
        prev = sum;
        sum += term;
        i++;
      }
    } else {
      // The computed CDF is too big so we substract from it.
      sum -= term;
      while (sum >= u) {
        term *= i / (q * (n + i - 1.0));
        i--;
        sum -= term;
      }
    }
    return i;
  }
 /**
  * Returns the standard deviation of the hypoexponential distribution with rates <SPAN
  * CLASS="MATH"><I>&#955;</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> -
  * 1</SPAN>]</TT>, <SPAN CLASS="MATH"><I>i</I> = 1,&#8230;, <I>k</I></SPAN>.
  *
  * @param lambda rates of the hypoexponential distribution
  * @return standard deviation of the hypoexponential distribution
  */
 public static double getStandardDeviation(double[] lambda) {
   double s = getVariance(lambda);
   return Math.sqrt(s);
 }