{ ****************************************************************** Inverses of incomplete Gamma function and Khi-2 distribution Translated from C code in Cephes library (http://www.moshier.net) ****************************************************************** } unit uinvgam; interface uses utypes, ugamma, uigamma, uinvnorm; function InvGamma(A, P : Float) : Float; { ------------------------------------------------------------------ Given P, the function finds X such that IGamma(A, X) = P It is best valid in the right-hand tail of the distribution, P > 0.5 ------------------------------------------------------------------ } function InvKhi2(Nu : Integer; P : Float) : Float; { ------------------------------------------------------------------ Inverse of Khi-2 distribution function Returns the argument, X, for which the area under the Khi-2 probability density function (integrated from 0 to X) is equal to P. ------------------------------------------------------------------ } implementation function InvGamma(A, P : Float) : Float; var x0, x1, x, Y, yl, yh, y1, d, lgm, dithresh : Float; i, ndir : Integer; label ihalve, cont, cont1, done; begin if P > 0.5 then SetErrCode(FPLoss) else SetErrCode(FOk); Y := 1.0 - P; { Bound the solution } x0 := MaxNum; yl := 0.0; x1 := 0.0; yh := 1.0; dithresh := 5 * MachEp; { Approximation to inverse function } d := 1.0 / (9.0 * a); y1 := 1.0 - d - InvNorm(Y) * sqrt(d); x := a * y1 * y1 * y1; lgm := LnGamma(a); for i := 0 to 9 do begin if (x > x0) or (x < x1) then goto ihalve; y1 := JGamma(a, x); if (y1 < yl) or (y1 > yh) then goto ihalve; if y1 < Y then begin x0 := x; yl := y1 end else begin x1 := x; yh := y1 end; { Compute the derivative of the function at this point } d := (a - 1) * Ln(x) - x - lgm; if d < MinLog then goto ihalve; d := -exp(d); { Compute the step to the next approximation of x } d := (y1 - Y) / d; if abs(d / x) < MachEp then goto done; x := x - d; end; { Resort to interval halving if Newton iteration did not converge } ihalve: d := 0.0625; if x0 = MaxNum then begin if x <= 0 then x := 1; while x0 = MaxNum do begin x := (1 + d) * x; y1 := JGamma(a, x); if y1 < Y then begin x0 := x; yl := y1; goto cont end; d := d + d end end; cont: d := 0.5; ndir := 0; for i := 0 to 399 do begin x := x1 + d * (x0 - x1); y1 := JGamma(a, x); lgm := (x0 - x1) / (x1 + x0); if abs(lgm) < dithresh then goto cont1; lgm := (y1 - Y) / Y; if abs(lgm) < dithresh then goto cont1; if x <= 0 then goto cont1; if y1 >= Y then begin x1 := x; yh := y1; if ndir < 0 then begin ndir := 0; d := 0.5 end else if ndir > 1 then d := 0.5 * d + 0.5 else d := (Y - yl) / (yh - yl); ndir := ndir + 1; end else begin x0 := x; yl := y1; if ndir > 0 then begin ndir := 0; d := 0.5 end else if ndir < -1 then d := 0.5 * d else d := (Y - yl) / (yh - yl); ndir := ndir - 1; end; end; cont1: if x = 0 then SetErrCode(FUnderflow); done: InvGamma := x end; function InvKhi2(Nu : Integer; P : Float) : Float; { ------------------------------------------------------------------ Inverse of Khi-2 distribution function Returns the argument, X, for which the area under the Khi-2 probability density function (integrated from 0 to X) is equal to P. ------------------------------------------------------------------ } begin if (P < 0.0) or (P > 1.0) or (Nu < 1) then InvKhi2 := DefaultVal(FDomain, 0.0) else InvKhi2 := 2.0 * InvGamma(0.5 * Nu, P); end; end.