double BetaIncompleteInvImpl()

in absl/random/internal/distribution_test_util.cc [256:363]


double BetaIncompleteInvImpl(const double p, const double q, const double beta,
                             const double alpha) {
  if (alpha < 0.5) {
    // Inverse Incomplete beta function is symmetrical, return the complement.
    return 1. - BetaIncompleteInvImpl(q, p, beta, 1. - alpha);
  }
  const double kErr = 1e-14;
  double value = kErr;

  // Compute the initial estimate.
  {
    double r = std::sqrt(-std::log(alpha * alpha));
    double y =
        r - fma(r, 0.27061, 2.30753) / fma(r, fma(r, 0.04481, 0.99229), 1.0);
    if (p > 1. && q > 1.) {
      r = (y * y - 3.) / 6.;
      double s = 1. / (p + p - 1.);
      double t = 1. / (q + q - 1.);
      double h = 2. / s + t;
      double w =
          y * std::sqrt(h + r) / h - (t - s) * (r + 5. / 6. - t / (3. * h));
      value = p / (p + q * std::exp(w + w));
    } else {
      r = q + q;
      double t = 1.0 / (9. * q);
      double u = 1.0 - t + y * std::sqrt(t);
      t = r * (u * u * u);
      if (t <= 0) {
        value = 1.0 - std::exp((std::log((1.0 - alpha) * q) + beta) / q);
      } else {
        t = (4.0 * p + r - 2.0) / t;
        if (t <= 1) {
          value = std::exp((std::log(alpha * p) + beta) / p);
        } else {
          value = 1.0 - 2.0 / (t + 1.0);
        }
      }
    }
  }

  // Solve for x using a modified newton-raphson method using the function
  // BetaIncomplete.
  {
    value = std::max(value, kErr);
    value = std::min(value, 1.0 - kErr);

    const double r = 1.0 - p;
    const double t = 1.0 - q;
    double y;
    double yprev = 0;
    double sq = 1;
    double prev = 1;
    for (;;) {
      if (value < 0 || value > 1.0) {
        // Error case; value went infinite.
        return std::numeric_limits<double>::infinity();
      } else if (value == 0 || value == 1) {
        y = value;
      } else {
        y = BetaIncompleteImpl(value, p, q, beta);
        if (!std::isfinite(y)) {
          return y;
        }
      }
      y = (y - alpha) *
          std::exp(beta + r * std::log(value) + t * std::log(1.0 - value));
      if (y * yprev <= 0) {
        prev = std::max(sq, std::numeric_limits<double>::min());
      }
      double g = 1.0;
      for (;;) {
        const double adj = g * y;
        const double adj_sq = adj * adj;
        if (adj_sq >= prev) {
          g = g / 3.0;
          continue;
        }
        const double tx = value - adj;
        if (tx < 0 || tx > 1) {
          g = g / 3.0;
          continue;
        }
        if (prev < kErr) {
          return value;
        }
        if (y * y < kErr) {
          return value;
        }
        if (tx == value) {
          return value;
        }
        if (tx == 0 || tx == 1) {
          g = g / 3.0;
          continue;
        }
        value = tx;
        yprev = y;
        break;
      }
    }
  }

  // NOTES: See also: Asymptotic inversion of the incomplete beta function.
  // https://core.ac.uk/download/pdf/82140723.pdf
  //
  // NOTE: See the Boost library documentation as well:
  // https://www.boost.org/doc/libs/1_52_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_beta/ibeta_function.html
}