Viewing contents of file '../idllib/contrib/meron/romberg.pro'
Function Romberg, fun, range, eps, relative = rel, params = pars, $
    try = ntry, error = erv, status = stat

;+
; NAME:
;    ROMBERG
; VERSION:
;	3.0
; PURPOSE:
;	Performs high precision numerical integration.
; CATEGORY:
;	Mathematical function (general).
; CALLING SEQUENCE:
;	Result = ROMBERG( FUN, RANGE [, EPS [, keywords]])
; INPUTS:
;    FUN
;	Character value representing an existing IDL function.  The function
;	must comply with the IDL standard of being able to accept an array 
;	input and return an array output.  The calling sequence for the 
;	function must be either
;	    Result = FUN(x)
;	or
;	    Result = FUN(x, extra)
;	where X is the variable and EXTRA may be any single entity (scalar, 
;	array, structure etc.) used to pass additional parameters to the 
;	function.
;    RANGE
;	Two element vector, integration range.
; OPTIONAL INPUT PARAMETERS:
;    EPS
;	Allowed integration error.  Default is around 1e-7 for single-precision 
;	integration and 2e-16 for double-precision.  EPS is understood to 
;	represent absolute error unless the keyword RELATIVE is set.
; KEYWORD PARAMETERS:
;    /RELATIVE
;	If set, EPS represent the allowed relative integration error.
;    PARAMS
;	An arbitrary value or variable which is passed to the function FUN.
;    TRY
;	Normally ROMBERG exits, with STATUS = 2, if the calculation error 
;	starts to grow before the convergence criterion is satisfied.  Setting
;	TRY to an integer > 0 specifies the number of additional attempts at
;	convergence before exit (useful with ill-conditioned functions).  The
;	default value is 0.
;    ERROR
;	Optional output, see below.
;    STATUS
;	Optional output, see below.
; OUTPUTS:
;	Returns the value of the integral.  The result is always a scalar. The
;	numerical type of the result (floating, double-precision or complex) is
;	determined by the type of values returned by FUN.
; OPTIONAL OUTPUT PARAMETERS:
;    ERROR
;	The name of the variable to receive the estimated integration error.
;	If RELATIVE is set the error returned is relative.
;    STATUS
;	The name of the variable to receive integration status information.
;	Possible values are:
;	    0 - Integration didn't converge.
;	    1 - OK.
;	    2 - Integration converged, but with precision worse then specified.
; COMMON BLOCKS:
;	None.
; SIDE EFFECTS:
;	None.
; RESTRICTIONS:
;	None.
; PROCEDURE:
;	Enhanced Romberg integration, using the extended midpoint rule and
;	Neville's interpolation algorithm.  The process is iterated untill 
;	either the desired accuracy is achieved, the maximal allowed number of
;	steps is exceeded or further iterations cause the error to grow instead
;	of diminishing (the last can be postponed using the TRY keyword).  The 
;	procedure can handle functions with an integrable singularity at one 
;	(or both) end of the integration interval.
;	Uses CAST, DEFAULT, FPU_FIX, ISNUM and TOLER from MIDL.
; MODIFICATION HISTORY:
;	Created 15-FEB-92 by Mati Meron.
;	Modified 20-JAN-1994 by Mati Meron.  Added keyword TRY.
;	Modified 5-OCT-1998 by Mati Meron.  Underflow filtering added.
;-

    on_error, 1
    stat = 0

    rex = Cast(range(1) - range(0),4)
    xc = range(0) + 0.5*rex
    relf = keyword_set(rel)
    ntr = Default(ntry,0,/dtype) > 0
    pflag = n_elements(pars) ne 0
    if pflag then fc = FPU_fix(rex/3*call_function(fun,xc,pars)) $
    else fc = FPU_fix(rex/3*call_function(fun,xc))
    isdob = Isnum(fc,/double,typ = ftyp)
    ftyp = ftyp > 4
    etyp = 4 + isdob
    sinf = machar(double = isdob)
    eps = abs(Default(eps,Toler(type = ftyp),/dtype))
    kmax = floor(-alog(Toler()*(0.5 + abs(xc/rex)))/alog(3)) > 0

    p = make_array(kmax + 1,type = ftyp)
    q = p
    res = p
    err = make_array(kmax + 1,type = etyp, value = sinf.xmax)
    k = 0
    kl = 1

    while (k lt kmax and err(k) ge eps) do begin
	k = k + 1
	rex = rex/3
	xl = xc - rex
	xr = xc + rex
	if pflag then begin
	    fl = total(FPU_fix(rex*call_function(fun,xl,pars)))
	    fr = total(FPU_fix(rex*call_function(fun,xr,pars)))
	endif else begin
	    fl = total(FPU_fix(rex*call_function(fun,xl)))
	    fr = total(FPU_fix(rex*call_function(fun,xr)))
	endelse
	xc = reform(transpose([[xl],[xc],[xr]]),3l^k)
	p(k) = 2*fc - fl - fr
	q(k) = fc + fl + fr
	fc = q(k)/3

	if p(k) eq 0 then begin
	    res(k) = q(k)
	    err(k) = 0
	endif else begin
	    l = k
	    while l gt kl do begin
		l = l - 1
		if p(l) eq p(k) then q(l) = q(l+1) else $
		q(l) = (p(l)*q(l+1) - p(k)*q(l))/(p(l) - p(k))
		nerr = abs(q(l+1) - q(l))
		if nerr gt err(k) then kl = l + 1 else err(k) = nerr
	    endwhile
	    res(k) = q(kl)
	    if relf and err(k) lt abs(res(k)) then err(k) = err(k)/abs(res(k))
	    if k gt kl + 1 then begin
		if err(k-1) lt err(k) < err(k-2) and ntr eq 0 then begin
		    k = k - 1
		    kmax = k
		    stat = 2
		endif else ntr = ntr - 1
	    endif
	endelse
    endwhile

    erv = FPU_fix(err(k))
    if erv lt eps then stat = 1
    return, FPU_fix(res(k))
end