// Copyright ©2016 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* * Cephes Math Library Release 2.4: March,1996 * Copyright 1984, 1996 by Stephen L. Moshier */ package cephes import "math" // Incbi computes the inverse of the regularized incomplete beta integral. func Incbi(aa, bb, yy0 float64) float64 { var a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt float64 var i, rflg, dir, nflg int i = 0 if yy0 <= 0 { return (0.0) } if yy0 >= 1.0 { return (1.0) } x0 = 0.0 yl = 0.0 x1 = 1.0 yh = 1.0 nflg = 0 if aa <= 1.0 || bb <= 1.0 { dithresh = 1.0e-6 rflg = 0 a = aa b = bb y0 = yy0 x = a / (a + b) y = Incbet(a, b, x) goto ihalve } else { dithresh = 1.0e-4 } // Approximation to inverse function yp = -Ndtri(yy0) if yy0 > 0.5 { rflg = 1 a = bb b = aa y0 = 1.0 - yy0 yp = -yp } else { rflg = 0 a = aa b = bb y0 = yy0 } lgm = (yp*yp - 3.0) / 6.0 x = 2.0 / (1.0/(2.0*a-1.0) + 1.0/(2.0*b-1.0)) d = yp*math.Sqrt(x+lgm)/x - (1.0/(2.0*b-1.0)-1.0/(2.0*a-1.0))*(lgm+5.0/6.0-2.0/(3.0*x)) d = 2.0 * d if d < minLog { // mtherr("incbi", UNDERFLOW) x = 0 goto done } x = a / (a + b*math.Exp(d)) y = Incbet(a, b, x) yp = (y - y0) / y0 if math.Abs(yp) < 0.2 { goto newt } /* Resort to interval halving if not close enough. */ ihalve: dir = 0 di = 0.5 for i = 0; i < 100; i++ { if i != 0 { x = x0 + di*(x1-x0) if x == 1.0 { x = 1.0 - machEp } if x == 0.0 { di = 0.5 x = x0 + di*(x1-x0) if x == 0.0 { // mtherr("incbi", UNDERFLOW) goto done } } y = Incbet(a, b, x) yp = (x1 - x0) / (x1 + x0) if math.Abs(yp) < dithresh { goto newt } yp = (y - y0) / y0 if math.Abs(yp) < dithresh { goto newt } } if y < y0 { x0 = x yl = y if dir < 0 { dir = 0 di = 0.5 } else if dir > 3 { di = 1.0 - (1.0-di)*(1.0-di) } else if dir > 1 { di = 0.5*di + 0.5 } else { di = (y0 - y) / (yh - yl) } dir += 1 if x0 > 0.75 { if rflg == 1 { rflg = 0 a = aa b = bb y0 = yy0 } else { rflg = 1 a = bb b = aa y0 = 1.0 - yy0 } x = 1.0 - x y = Incbet(a, b, x) x0 = 0.0 yl = 0.0 x1 = 1.0 yh = 1.0 goto ihalve } } else { x1 = x if rflg == 1 && x1 < machEp { x = 0.0 goto done } yh = y if dir > 0 { dir = 0 di = 0.5 } else if dir < -3 { di = di * di } else if dir < -1 { di = 0.5 * di } else { di = (y - y0) / (yh - yl) } dir -= 1 } } // mtherr("incbi", PLOSS) if x0 >= 1.0 { x = 1.0 - machEp goto done } if x <= 0.0 { // mtherr("incbi", UNDERFLOW) x = 0.0 goto done } newt: if nflg > 0 { goto done } nflg = 1 lgm = lgam(a+b) - lgam(a) - lgam(b) for i = 0; i < 8; i++ { /* Compute the function at this point. */ if i != 0 { y = Incbet(a, b, x) } if y < yl { x = x0 y = yl } else if y > yh { x = x1 y = yh } else if y < y0 { x0 = x yl = y } else { x1 = x yh = y } if x == 1.0 || x == 0.0 { break } /* Compute the derivative of the function at this point. */ d = (a-1.0)*math.Log(x) + (b-1.0)*math.Log(1.0-x) + lgm if d < minLog { goto done } if d > maxLog { break } d = math.Exp(d) /* Compute the step to the next approximation of x. */ d = (y - y0) / d xt = x - d if xt <= x0 { y = (x - x0) / (x1 - x0) xt = x0 + 0.5*y*(x-x0) if xt <= 0.0 { break } } if xt >= x1 { y = (x1 - x) / (x1 - x0) xt = x1 - 0.5*y*(x1-x) if xt >= 1.0 { break } } x = xt if math.Abs(d/x) < 128.0*machEp { goto done } } /* Did not converge. */ dithresh = 256.0 * machEp goto ihalve done: if rflg > 0 { if x <= machEp { x = 1.0 - machEp } else { x = 1.0 - x } } return (x) } func lgam(a float64) float64 { lg, _ := math.Lgamma(a) return lg }