github.com/gopherd/gonum@v0.0.4/mathext/internal/cephes/incbi.go (about)

     1  // Copyright ©2016 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6   * Cephes Math Library Release 2.4:  March,1996
     7   * Copyright 1984, 1996 by Stephen L. Moshier
     8   */
     9  
    10  package cephes
    11  
    12  import "math"
    13  
    14  // Incbi computes the inverse of the regularized incomplete beta integral.
    15  func Incbi(aa, bb, yy0 float64) float64 {
    16  	var a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt float64
    17  	var i, rflg, dir, nflg int
    18  
    19  	if yy0 <= 0 {
    20  		return (0.0)
    21  	}
    22  	if yy0 >= 1.0 {
    23  		return (1.0)
    24  	}
    25  	x0 = 0.0
    26  	yl = 0.0
    27  	x1 = 1.0
    28  	yh = 1.0
    29  	nflg = 0
    30  
    31  	if aa <= 1.0 || bb <= 1.0 {
    32  		dithresh = 1.0e-6
    33  		rflg = 0
    34  		a = aa
    35  		b = bb
    36  		y0 = yy0
    37  		x = a / (a + b)
    38  		y = Incbet(a, b, x)
    39  		goto ihalve
    40  	} else {
    41  		dithresh = 1.0e-4
    42  	}
    43  	// Approximation to inverse function
    44  	yp = -Ndtri(yy0)
    45  
    46  	if yy0 > 0.5 {
    47  		rflg = 1
    48  		a = bb
    49  		b = aa
    50  		y0 = 1.0 - yy0
    51  		yp = -yp
    52  	} else {
    53  		rflg = 0
    54  		a = aa
    55  		b = bb
    56  		y0 = yy0
    57  	}
    58  
    59  	lgm = (yp*yp - 3.0) / 6.0
    60  	x = 2.0 / (1.0/(2.0*a-1.0) + 1.0/(2.0*b-1.0))
    61  	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))
    62  	d = 2.0 * d
    63  	if d < minLog {
    64  		// mtherr("incbi", UNDERFLOW)
    65  		x = 0
    66  		goto done
    67  	}
    68  	x = a / (a + b*math.Exp(d))
    69  	y = Incbet(a, b, x)
    70  	yp = (y - y0) / y0
    71  	if math.Abs(yp) < 0.2 {
    72  		goto newt
    73  	}
    74  
    75  	/* Resort to interval halving if not close enough. */
    76  ihalve:
    77  
    78  	dir = 0
    79  	di = 0.5
    80  	for i = 0; i < 100; i++ {
    81  		if i != 0 {
    82  			x = x0 + di*(x1-x0)
    83  			if x == 1.0 {
    84  				x = 1.0 - machEp
    85  			}
    86  			if x == 0.0 {
    87  				di = 0.5
    88  				x = x0 + di*(x1-x0)
    89  				if x == 0.0 {
    90  					// mtherr("incbi", UNDERFLOW)
    91  					goto done
    92  				}
    93  			}
    94  			y = Incbet(a, b, x)
    95  			yp = (x1 - x0) / (x1 + x0)
    96  			if math.Abs(yp) < dithresh {
    97  				goto newt
    98  			}
    99  			yp = (y - y0) / y0
   100  			if math.Abs(yp) < dithresh {
   101  				goto newt
   102  			}
   103  		}
   104  		if y < y0 {
   105  			x0 = x
   106  			yl = y
   107  			if dir < 0 {
   108  				dir = 0
   109  				di = 0.5
   110  			} else if dir > 3 {
   111  				di = 1.0 - (1.0-di)*(1.0-di)
   112  			} else if dir > 1 {
   113  				di = 0.5*di + 0.5
   114  			} else {
   115  				di = (y0 - y) / (yh - yl)
   116  			}
   117  			dir += 1
   118  			if x0 > 0.75 {
   119  				if rflg == 1 {
   120  					rflg = 0
   121  					a = aa
   122  					b = bb
   123  					y0 = yy0
   124  				} else {
   125  					rflg = 1
   126  					a = bb
   127  					b = aa
   128  					y0 = 1.0 - yy0
   129  				}
   130  				x = 1.0 - x
   131  				y = Incbet(a, b, x)
   132  				x0 = 0.0
   133  				yl = 0.0
   134  				x1 = 1.0
   135  				yh = 1.0
   136  				goto ihalve
   137  			}
   138  		} else {
   139  			x1 = x
   140  			if rflg == 1 && x1 < machEp {
   141  				x = 0.0
   142  				goto done
   143  			}
   144  			yh = y
   145  			if dir > 0 {
   146  				dir = 0
   147  				di = 0.5
   148  			} else if dir < -3 {
   149  				di = di * di
   150  			} else if dir < -1 {
   151  				di = 0.5 * di
   152  			} else {
   153  				di = (y - y0) / (yh - yl)
   154  			}
   155  			dir -= 1
   156  		}
   157  	}
   158  	// mtherr("incbi", PLOSS)
   159  	if x0 >= 1.0 {
   160  		x = 1.0 - machEp
   161  		goto done
   162  	}
   163  	if x <= 0.0 {
   164  		// mtherr("incbi", UNDERFLOW)
   165  		x = 0.0
   166  		goto done
   167  	}
   168  
   169  newt:
   170  	if nflg > 0 {
   171  		goto done
   172  	}
   173  	nflg = 1
   174  	lgm = lgam(a+b) - lgam(a) - lgam(b)
   175  
   176  	for i = 0; i < 8; i++ {
   177  		/* Compute the function at this point. */
   178  		if i != 0 {
   179  			y = Incbet(a, b, x)
   180  		}
   181  		if y < yl {
   182  			x = x0
   183  			y = yl
   184  		} else if y > yh {
   185  			x = x1
   186  			y = yh
   187  		} else if y < y0 {
   188  			x0 = x
   189  			yl = y
   190  		} else {
   191  			x1 = x
   192  			yh = y
   193  		}
   194  		if x == 1.0 || x == 0.0 {
   195  			break
   196  		}
   197  		/* Compute the derivative of the function at this point. */
   198  		d = (a-1.0)*math.Log(x) + (b-1.0)*math.Log(1.0-x) + lgm
   199  		if d < minLog {
   200  			goto done
   201  		}
   202  		if d > maxLog {
   203  			break
   204  		}
   205  		d = math.Exp(d)
   206  		/* Compute the step to the next approximation of x. */
   207  		d = (y - y0) / d
   208  		xt = x - d
   209  		if xt <= x0 {
   210  			y = (x - x0) / (x1 - x0)
   211  			xt = x0 + 0.5*y*(x-x0)
   212  			if xt <= 0.0 {
   213  				break
   214  			}
   215  		}
   216  		if xt >= x1 {
   217  			y = (x1 - x) / (x1 - x0)
   218  			xt = x1 - 0.5*y*(x1-x)
   219  			if xt >= 1.0 {
   220  				break
   221  			}
   222  		}
   223  		x = xt
   224  		if math.Abs(d/x) < 128.0*machEp {
   225  			goto done
   226  		}
   227  	}
   228  	/* Did not converge.  */
   229  	dithresh = 256.0 * machEp
   230  	goto ihalve
   231  
   232  done:
   233  
   234  	if rflg > 0 {
   235  		if x <= machEp {
   236  			x = 1.0 - machEp
   237  		} else {
   238  			x = 1.0 - x
   239  		}
   240  	}
   241  	return (x)
   242  }
   243  
   244  func lgam(a float64) float64 {
   245  	lg, _ := math.Lgamma(a)
   246  	return lg
   247  }