Repository: scipy/scipy
File: scipy/stats/_binomtest.py (Python)

Code before:
        def k_lt_pn(d, k, p, n):
            B = _SimpleBinomial(n, p)
            ix = _binary_search_for_binom_tst(lambda x1: -B.pmf(x1),
                                              -d*rerr, np.ceil(p * n), n)
            # y is the number of terms between mode and n that are <= d*rerr.
            # ix gave us the first term where a(ix) <= d*rerr < a(ix-1)
            # if the first equality doesn't hold, y=n-ix. Otherwise, we
            # need to include ix as well as the equality holds. Note that
            # the equality will hold in very very rare situations due to rerr.
            y = n - ix + np.asarray(d*rerr == B.pmf(ix), dtype=ix.dtype)
            pval = B.cdf(k) + B.sf(n - y)
            return pval

        def k_gte_pn(d, k, p, n):
            B = _SimpleBinomial(n, p)
            ix = _binary_search_for_binom_tst(B.pmf,
                                              d*rerr, np.zeros_like(n), np.floor(p * n))
            # y is the number of terms between 0 and mode that are <= d*rerr.
            # we need to add a 1 to account for the 0 index.
            # For comparing this with old behavior, see
            # tst_binary_srch_for_binom_tst method in test_morestats.
            y = ix + 1
            pval = B.cdf(y-1) + B.sf(k-1)
            return pval

        pval = xpx.apply_where(k < p * n, (d, k, p, n), k_lt_pn,  k_gte_pn)
        pval = np.minimum(1.0, pval)

    statistic = np.where(valid, k/n, np.nan)
    pval = np.where(valid, pval, np.nan)
    result = BinomTestResult(k=k, n=n, alternative=alternative,
                             statistic=statistic, pvalue=pval)
    return result


def _binary_search_for_binom_tst(a, d, lo, hi):
    """
    Conducts an implicit binary search on a function specified by `a`.

    Meant to be used on the binomial PMF for the case of two-sided tests
    to obtain the value on the other side of the mode where the tail
    probability should be computed. The values on either side of
    the mode are always in order, meaning binary search is applicable.

    Parameters
    ----------
    a : callable
      The function over which to perform binary search. Its values
      for inputs lo and hi should be in ascending order.
    d : float
      The value to search.

Review comment (suggestion):
```suggestion
        pval = xpx.apply_where(k < p*n, (d, k, p, n), k_lt_pn,  k_gte_pn)
```
Probably should check the  `k == p*n` is tested.

Code after:
        # alternative is 'two-sided'
        d = B.pmf(k)
        rerr = 1 + 1e-7

        def k_lt_pn(d, k, p, n):
            B = _SimpleBinomial(n, p)
            ix = _binary_search_for_binom_tst(lambda x1: -B.pmf(x1),
                                              -d*rerr, np.ceil(p * n), n)
            # y is the number of terms between mode and n that are <= d*rerr.
            # ix gave us the first term where a(ix) <= d*rerr < a(ix-1)
            # if the first equality doesn't hold, y=n-ix. Otherwise, we
            # need to include ix as well as the equality holds. Note that
            # the equality will hold in very very rare situations due to rerr.
            y = n - ix + np.asarray(d*rerr == B.pmf(ix), dtype=ix.dtype)
            pval = B.cdf(k) + B.sf(n - y)
            return pval

        def k_gte_pn(d, k, p, n):
            B = _SimpleBinomial(n, p)
            ix = _binary_search_for_binom_tst(B.pmf,
                                              d*rerr, np.zeros_like(n), np.floor(p * n))
            # y is the number of terms between 0 and mode that are <= d*rerr.
            # we need to add a 1 to account for the 0 index.
            # For comparing this with old behavior, see
            # tst_binary_srch_for_binom_tst method in test_morestats.
            y = ix + 1
            pval = B.cdf(y-1) + B.sf(k-1)
            return pval

        pval = xpx.apply_where(k < p*n, (d, k, p, n), k_lt_pn,  k_gte_pn)
        pval = np.minimum(1.0, pval)

    statistic = np.where(valid, k/n, np.nan)
    pval = np.where(valid, pval, np.nan)
    result = BinomTestResult(k=k[()], n=n[()], alternative=alternative,
                             statistic=statistic[()], pvalue=pval[()])
    return result


def _binary_search_for_binom_tst(a, d, lo, hi):
    """
    Conducts an implicit binary search on a function specified by `a`.

    Meant to be used on the binomial PMF for the case of two-sided tests
    to obtain the value on the other side of the mode where the tail
    probability should be computed. The values on either side of
    the mode are always in order, meaning binary search is applicable.

    Parameters
    ----------
    a : callable