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 }