MODULE Oscillating_Integration
! Code converted using TO_F90 by Alan Miller
! Date: 2002-05-18  Time: 18:20:57

!     ALGORITHM 639 COLLECTED ALGORITHMS FROM ACM.
!     ALGORITHM APPEARED IN ACM-TRANS. MATH. SOFTWARE, VOL.12, NO. 1,
!     MAR., 1986, P. 24.

IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)


CONTAINS


! **************** BEGIN SUBROUTINE OSCINT ****************

SUBROUTINE oscint(azero, period, rfirst, eps, nquad, ndim1, ndim2, gauss,  &
                  hfun, gper, savper, weight, abscis, qlist, result, istate)


! N.B. Argument WORK has been removed.

!CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

!      THIS ROUTINE PROVIDES AN APPROXIMATION **RESULT** TO THE INTEGRAL OF
!  AN OVERALL INTEGRAND **HFUN(X)*GPER(X)** OVER A SEMI-INFINITE INTERVAL
!  (AZERO,INFINITY).  THE OVERALL INTEGRAND FUNCTION SHOULD BE ULTIMATELY
!  OSCILLATING IN SIGN AND PERIODIC WITH USER-PROVIDED **PERIOD**.

!      THE ROUTINE CONSIDERS A SEQUENCE OF INTERVALS.  ALL THE INTERVALS,
!  EXCEPT THE FIRST, ARE OF LENGTH 0.5*PERIOD.   IT USES A QUADRATURE RULE TO
!  APPROXIMATE THE INTEGRAL OVER EACH INTERVAL.  THESE APPROXIMATIONS ARE
!  STORED IN QLIST(J),  J = 1,2,3,...  .  THE VALUES OF QLIST ULTIMATELY
!  OSCILLATE IN SIGN.  THEN THE ROUTINE USES A SERIES ACCELERATION
!  TECHNIQUE BASED ON THE EULER TRANSFORMATION TO SUM THIS SEQUENCE.


! INPUT PARAMETERS:

!   AZERO         LOWER LIMIT OF INTEGRATION

!   PERIOD        LEAST POSITIVE PERIOD OR ULTIMATE PERIOD OF
!                 INTEGRAND FUNCTION

!   RFIRST        RIGHT-HAND ENDPOINT OF FIRST INTERVAL. SUGGESTED VALUE
!                 FOR STRAIGHTFORWARD PROBLEMS IS AZERO.
!                 SEE NOTE 1 BELOW

!   EPS           THE REQUESTED ACCURACY

!   NQUAD         INTEGER. NQ = ABS(NQUAD) IS THE NUMBER OFABSCISSAS TO
!                 BE USED BY THE QUADRATURE RULE IN EACH INTERVAL.
!                     NQUAD > 1    TRAPEZOIDAL RULE IS USED
!                     NQUAD < 0    RULE SPECIFIED BY GAUSS BELOW IS USED

!   NDIM1,NDIM2   DIMENSIONS OF THE OUTPUT ARRAY **WORK**.
!                 NDIM1 = 10 IMPLIES NMAX = 100
!                 NDIM1 .NE. 10 IMPLIES NMAX = MIN(100,NDIM1)
!                 NMAX IS A PHYSICAL LIMIT.  IF CALCULATION IS NOT
!                 COMPLETE AFTER NMAX INTERVALS HAVE BEEN CONSIDERED
!                 IT IS THEN ABANDONED. (SEE NOTE 5 BELOW)
!                 NDIM2  SUGGESTED VALUE IS 15.  NORMALLY SHOULD EXCEED 4

!   GAUSS         NAME OF A SUBROUTINE WHICH PROVIDES WEIGHTS
!                 AND ABSCISSAS.  WHEN NQUAD > 0 THIS IS NOT CALLED.
!                 SEE NOTE 2 BELOW FOR USE WHEN NQUAD < 0.

!   HFUN          NAME OF A FUNCTION SUBROUTINE.  THIS IS ONE FACTOR OF
!                 THE INTEGRAND FUNCTION.  THE OTHER FACTOR IS GPER BELOW

!   GPER          NAME OF A FUNCTION SUBROUTINE.  THIS IS THE
!                 CO-FACTOR OF HFUN IN THE INTEGRAND FUNCTION.
!                 GPER MUST BE PERIODIC WITH PERIOD COINCIDING WITH THE
!                 INPUT PARAMETER, PERIOD ABOVE. (SEE NOTE 3 BELOW)


! OUTPUT PARAMETERS:

!   WORK(NDIM1,NDIM2)    HOLDS COMPLETED FINITE AVERAGE TABLE

!   SAVPER( )         ARRAY TO SAVE FUNCTION VALUES OF GPER.
!                     DIMENSION NOT LESS THAN 2*ABS(NQUAD)


!   WEIGHT( )         ARRAY TO HOLD WEIGHTS FOR GAUSSIAN QUADRATURE
!                     DIMENSION NOT LESS THAN MAX(1,-NQUAD)

!   ABSCIS( )         ARRAY TO HOLD ABSCISSAS FOR GAUSSIAN QUADRATURE
!                     DIMENSION NOT LESS THAN MAX(1,-NQUAD)

!   QLIST(100)        ARRAY TO HOLD THE RESULT OF EACH QUADRATURE

!   RESULT            OVERALL RESULT OF THE INTEGRATION

!   ISTATE(6)         VECTOR OF INTEGERS GIVES STATUS OF RESULT

!     ISTATE(1)       AN INDICATOR WARNING ABOUT SUSPICIOUS RESULTS -
!                     ZERO:      THE RUN WAS APPARENTLY SUCCESSFUL.
!                     POSITIVE:  THE RUN WAS APPARENTLY SUCCESSFUL, BUT
!                                THERE ARE UNSATISFACTORY FEATURES OF
!                                POSSIBLE INTEREST TO THE SOPHISTICATED USER
!                     NEGATIVE:  UNSUCCESSFUL RUN - DISREGARD THE RESULT
!                     SEE NOTES BELOW FOR COMPLETE SPECIFICATION
!     ISTATE(2)       LSIGCH - INDICATES LAST INTERVAL IN WHICH THE SIGN
!                                OF THE INTEGRAL COINCIDED WITH THAT OF
!                                THE INTEGRAL OVER THE PREVIOUS INTERVAL
!                                SEE NOTE 4 BELOW.
!     ISTATE(3)       NOW  - THE NUMBER OF FINITE INTEGRALS, QLIST(Q),
!                                EVALUATED IN THE CALCULATION.
!     ISTATE(4)       NCOL - THE COLUMN OF THE FINITE AVERAGE TABLE,
!                                (WORK), ON WHICH THE  RESULT IS BASED.
!     ISTATE(5)       NROW - THE ROW OF THE FINITE AVERAGE TABLE,
!                                (WORK), ON WHICH THE  RESULT IS BASED.
!     ISTATE(6)       NCOUNT - THE NUMBER OF CALLS TO FUNCTION HFUN.



!  NOTE 1

!  RFIRST
!     THIS ALLOWS THE USER TO LOCATE HIS SUBDIVISION.  FOR CAUTIOUS RUNNING,
!  ARRANGE RFIRST TO COINCIDE WITH AN "ULTIMATE ZERO".  FOR SLIGHTLY MORE
!  ADVENTUROUS BUT LESS RELIABLE RUNNING, ARRANGE RFIRST TO COINCIDE WITH AN
!  "ULTIMATE" PEAK.  OTHERWISE, SET RFIRST < AZERO, IN WHICH CASE, OSCINT USES
!  AZERO INSTEAD OF  RFIRST.


!  NOTE 2

!  GAUSS
!     THIS IS THE NAME OF A USER-PROVIDED SUBROUTINE OF THE FORM
!          GAUSS(ITYPE, A, B, C, D, N, WEIGHT(N), ABSCIS(N), IFAIL)
!  IT IS CALLED ONLY WHEN NQUAD IS NEGATIVE WITH N = -NQUAD.  IT RETURNS A SET
!  OF WEIGHTS AND ABSCISSAS, SUITABLE FOR INTEGRATION OVER THE INTERVAL (-1,1).

!     THE FIRST FIVE INPUT PARAMETERS OF GAUSS SHOULD BE IGNORED.
!  IFAIL MAY BE USED FOR A WARNING MESSAGE.  IF GAUSS RETURNS
!  IFAIL .NE. 0, OSCINT ABORTS, SETTING ISTATE(1) = -4000.

!     THE USER WHO HAS THE NAG LIBRARY AVAILABLE MAY SET GAUSS = D01BCF.


!  NOTE 3

!  HFUN AND GPER
!     ONE MAY ALWAYS USE HFUN FOR THE INTEGRAND FUNCTION, F(X), AND CODE GPER
!  TO RETURN THE VALUE 1.0D0.  HOWEVER, WHEN F(X) HAS A PERIODIC FACTOR, J(X),
!  REPETITIVE EVALUATION OF J(X) IN EACH INTERVAL MAY BE AVOIDED BY SETTING
!  GPER = J(X) AND HFUN = H(X).   THE ROUTINE CHECKS THAT GPER IS INDEED
!  PERIODIC BY MAKING SOME EVALUATIONS IN THE THIRD, FOURTH, AND FIFTH
!  INTERVALS.  IF IT FINDS GPER IS NOT PERIODIC, IT TERMINATES WITH
!  ISTATE(1) = -5000.
!  IF PERIOD <= 10**-5, THE ROUTINE TERMINATES WITH ISTATE(1) = -3000.


!  NOTE 4

!  TERMINATION
!     IN THIS NOTE, THE "NORMAL SIGN CHANGE PATTERN" IS ONE IN WHICH
!  SUCCESSIVE VALUES OF QLIST(Q) OSCILLATE IN SIGN.  THE "GRACE PERIOD" IS
!  Q <= 10 WHEN THE NORMAL SIGN CHANGE PATTERN IS NOT INSISTED ON.
!  LSIGCH IS THE HIGHEST VALUE OF Q FOR WHICH QLIST(Q)*QLIST(Q-1) IS POSITIVE.
!  TERMINATION COMES ABOUT AFTER CALCULATING QLIST(NOW), WHEN EITHER

!  (A)  AN APPROXIMATION OF SUFFICIENT ACCURACY IS CURRENTLY AVAIL-
!       ABLE  (THE ROUTINE SETS ISTATE(1) = MAX(0,4-(NOW-LSIGCH))
!  OR

!  (B)  THE NORMAL SIGN CHANGE PATTERN IS VIOLATED AFTER THE GRACE
!       PERIOD.   (THE ROUTINE SETS ISTATE(1) = -200)
!   OR

!  (C)  THE USER SET LIMIT, NMAX, OF INTERVALS HAVE BEEN CALCULATED
!       I.E.  NOW = NMAX.  (THE ROUTINE SETS ISTATE(1) = -100)

!     THE ROUTINE CHECKS (A), THEN (B), AND THEN (C).  TERMINATION
!  UNDER (A) MAY OCCUR WITHOUT ANY NORMAL SIGN CHANGE PATTERN
!  EMERGING.  THIS MAY BE DUE TO MISUSE OF THE ROUTINE, BUT THE
!  PROBLEM IS SMALL ENOUGH TO BE CORRECTLY HANDLED.  IN THIS CASE,
!  ISTATE(1) = MAX(0,4-(NOW-LSIGCH)) MAY BE A SMALL POSITIVE  INTEGER.


!  NOTE 5

!  VARIABLE DIMENSIONS AND STORAGE ECONOMY
!     THE PROGRAM WHICH CALLS OSCINT HAS TO PROVIDE NUMERICAL VALUES OF THE
!  INPUT PARAMETERS NQUAD, NDIM1 AND NDIM2.  IT MUST ALSO INCLUDE A DIMENSION
!  STATEMENT IN WHICH THE FIRST FIVE AND THE LAST OUTPUT PARAMETERS OF OSCINT
!  ARE DIMENSIONED.  NOTE THAT THE DIMENSIONS OF WEIGHT AND ABSCIS ARE BOTH AT
!  LEAST 1 WHEN NQUAD IS POSITIVE AND AT LEAST -NQUAD OTHERWISE.  HOWEVER, THE
!  DIMENSION OF SAVPER IS AT LEAST 2*ABS(NQUAD) REGARDLESS OF THE SIGN OF NQUAD.

!     GENERALLY, THESE VARIABLE DIMENSION STATEMENTS ALLOW ECONOMIC USE OF
!  STORAGE.  THERE IS ONE FURTHER STORAGE SAVING FEATURE.
!  WHEN NDIM1 = 10, THE ROUTINE CARRIES OUT THE SAME CALCULATION AS IT WOULD
!  IF NDIM1 = 100.  HOWEVER, INSTEAD OF USING A WORK ARRAY OF DIMENSION
!  (100,NDIM2), IT USES A WORK ARRAY OF DIMENSION (10,NDIM2) AND OVERWRITES
!  IT, AS AND WHEN NECESSARY, AS THE CALCULATION PROCEEDS.
!  IN THIS CASE, OSCINT BEHAVES AS IF NDIM2 WERE REPLACED BY  MIN(20,NDIM2).

!     SOME OBVIOUS ERRORS IN DIMENSIONING, SUCH AS NEGATIVE DIMENSIONS,
!  CAUSE THE ROUTINE TO TERMINATE WITH
!         ISTATE(1) = -6000
!  HOWEVER, INADEQUATE DIMENSIONING IN THE CALLING PROGRAM MAY GO
!  UNDETECTED AND MAY LEAD TO RANDOM OR CHAOTIC OUTPUT.


!CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

REAL (dp), INTENT(IN)   :: azero
REAL (dp), INTENT(IN)   :: period
REAL (dp), INTENT(IN)   :: rfirst
REAL (dp), INTENT(IN)   :: eps
INTEGER, INTENT(IN)     :: nquad
INTEGER, INTENT(IN)     :: ndim1
INTEGER, INTENT(IN)     :: ndim2
REAL (dp), INTENT(OUT)  :: savper(:)
REAL (dp), INTENT(OUT)  :: weight(:)
REAL (dp), INTENT(OUT)  :: abscis(:)
REAL (dp), INTENT(OUT)  :: qlist(100)
REAL (dp), INTENT(OUT)  :: result
INTEGER, INTENT(OUT)    :: istate(6)

! EXTERNAL gauss, hfun, gper

INTERFACE
  SUBROUTINE GAUSS(ITYPE, A, B, C, D, N, WEIGHT, ABSCIS, IFAIL)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    INTEGER, INTENT(IN)     :: itype
    REAL (dp), INTENT(IN)   :: a, b, c, d
    INTEGER, INTENT(IN)     :: n
    REAL (dp), INTENT(OUT)  :: weight(:), abscis(:)
    INTEGER, INTENT(OUT)    :: ifail
  END SUBROUTINE GAUSS

  FUNCTION hfun(x) RESULT(fn_val)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp), INTENT(IN)  :: x
    REAL (dp)              :: fn_val
  END FUNCTION hfun

  FUNCTION gper(x) RESULT(fn_val)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp), INTENT(IN)  :: x
    REAL (dp)              :: fn_val
  END FUNCTION gper
END INTERFACE

REAL (dp)  :: curr, hasper, prev, s, wmin, work(ndim1,ndim2)
INTEGER    :: i, ii, isub, j, jp, k, mj, mj1, mj2, mjk, mjm2, nmax, nml,  &
              now, nowjp, nround, nrow

hasper = .5D0 * period
wmin = 1.0D0
istate(1:6) = 0
IF (nquad == 0 .OR. nquad == 1 .OR. ndim1 < 1 .OR. ndim2 < 1) istate(1) = -6000
IF (period < 10.0D-5) istate(1) = -5000
IF (istate(1) >= 0) THEN
  IF (ndim1 == 10) THEN
    nmax = 100
    
  ELSE
    nmax = MIN(100,ndim1)
  END IF
  
  jp = 0
  s = 0.0D0
  
!      LOOP TO CONSTRUCT TABLE
  
  20 IF (jp == nmax) istate(1) = -100
  IF (istate(1) == 0) THEN
    
    k = 0
    jp = jp + 1
    j = MIN(jp,ndim2)
    IF (ndim1 == 10) j = MIN(j,20)
    30 IF (k < j) THEN
      k = k + 1
      IF (ndim1 == 10) THEN
        mj = MOD(jp,10)
        mjm2 = MOD(jp-2,10)
        mj1 = MOD(jp-k+1,10)
        mj2 = MOD(jp-k+2,10)
        mjk = MOD(jp-k,10)
        IF (mj == 0) mj = 10
        IF (mjm2 == 0) mjm2 = 10
        IF (mj1 == 0) mj1 = 10
        IF (mj2 == 0) mj2 = 10
        IF (mjk == 0) mjk = 10
        
      ELSE
        mj = jp
        mjm2 = jp - 2
        mj1 = jp - k + 1
        mj2 = jp - k + 2
        mjk = jp - k
      END IF
      
      IF (k == 1) THEN
        work(mj,k) = qrule(jp-1, azero, hasper, rfirst, nquad, ndim1, ndim2, &
                           gauss, hfun, gper, savper, weight, abscis, qlist, &
                           istate)
        
      ELSE
        work(mj1,k) = (work(mj1,k-1) + work(mj2,k-1)) / 2.0D0
        IF (ABS(work(mj1,k)) < wmin) THEN
          wmin = ABS(work(mj1,k))
          now = k
          nrow = mj1
          nowjp = jp
        END IF
        
        curr = ABS(work(mj1,k))
        prev = ABS(work(mjk,k))
        IF (jp /= k) THEN
          IF (curr < eps .AND. prev < eps) THEN
            now = k
            nrow = mj1
            nowjp = jp
            GO TO 40
            
          END IF
          
        END IF
        
      END IF
      
      GO TO 30
      
    END IF
    
    GO TO 20
  END IF
  
  40 DO  i = 1, nowjp - now
    s = s + qlist(i)
  END DO
  IF (ndim1 == 10) THEN
    nround = now - 10
    IF (nround <= 0) nround = 0
    IF (nround > 0) THEN
      
      DO  i = nowjp - now + 1, nowjp - 10
        s = s + qlist(i)
      END DO
      isub = nowjp - 9
      70 IF (isub > 10) isub = isub - 10
      IF (isub > 10) GO TO 70
      DO  j = 1, nround
        s = s + work(isub,j) / 2.0D0
      END DO
      DO  i = nrow, nrow + nround - 1
        ii = i
        IF (i > 10) ii = i - 10
        s = s - work(ii,nround+1)
      END DO
    END IF
  END IF
  
  DO  j = nround + 1, now - 1
    s = s + work(nrow,j) / 2.0D0
  END DO
  s = s + work(nrow,now)
  result = s
  istate(3) = jp
  istate(4) = now
  istate(5) = nrow
  nml = istate(3) - istate(2)
  IF (nml < 4 .AND. istate(1) == 0) istate(1) = MAX(0,4-nml)
END IF
RETURN
END SUBROUTINE oscint

! ******** END OF OSCINT *********



! *********** BEGIN FUNCTION QRULE ************

FUNCTION qrule(j, azero, hasper, rfirst, nquad, ndim1, ndim2, gauss, hfun,  &
               gper, savper, weight, abscis, qlist, istate) RESULT(fn_val)

!CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

!          THIS ROUTINE EVALUATES THE INTEGRAL OF THE FUNCTION,
!     HFUN(X)*GPER(X) OVER THE INTERVAL (A,B) WHERE:

!          GENERALLY (J>0)     A = PREVIOUS VALUE OF B
!                              B = A + HASPER
!                    (J=0)     A = AZERO
!                              B = RFIRST WHEN RFIRST > AZERO
!                              B = AZERO + HASPER WHEN RFIRST <= AZERO

!      INPUT PARAMETERS:

!               J                  DEFINES WHICH TERM, QLIST(J), OF
!                                  THE SERIES IS BEING EVALUATED

!               HASPER             HALF THE PERIOD


!      OTHER INPUT AND OUTPUT PARAMETERS ARE IDENTICAL TO THOSE IN
!      OSCINT (DESCRIBED ABOVE).

!CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

INTEGER, INTENT(IN)     :: j
REAL (dp), INTENT(IN)   :: azero
REAL (dp), INTENT(IN)   :: hasper
REAL (dp), INTENT(IN)   :: rfirst
INTEGER, INTENT(IN)     :: nquad
INTEGER, INTENT(IN)     :: ndim1
INTEGER, INTENT(IN)     :: ndim2
REAL (dp), INTENT(OUT)  :: savper(:)
REAL (dp), INTENT(OUT)  :: weight(:)
REAL (dp), INTENT(OUT)  :: abscis(:)
REAL (dp), INTENT(OUT)  :: qlist(100)
INTEGER, INTENT(OUT)    :: istate(6)
REAL (dp)               :: fn_val

! SAVE b, tendpt
! EXTERNAL gauss, hfun, gper

INTERFACE
  SUBROUTINE GAUSS(ITYPE, A, B, C, D, N, WEIGHT, ABSCIS, IFAIL)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    INTEGER, INTENT(IN)     :: itype
    REAL (dp), INTENT(IN)   :: a, b, c, d
    INTEGER, INTENT(IN)     :: n
    REAL (dp), INTENT(OUT)  :: weight(:), abscis(:)
    INTEGER, INTENT(OUT)    :: ifail
  END SUBROUTINE GAUSS

  FUNCTION hfun(x) RESULT(fn_val)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp), INTENT(IN)  :: x
    REAL (dp)              :: fn_val
  END FUNCTION hfun

  FUNCTION gper(x) RESULT(fn_val)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp), INTENT(IN)  :: x
    REAL (dp)              :: fn_val
  END FUNCTION gper
END INTERFACE

INTEGER    :: i, ielm, ifail, INDEX, itype, npts
REAL (dp)  :: a, aa, bb, cc, dd, diff, fun, gmax, xi, y, wsum, wt
REAL (dp), SAVE  :: b, tendpt

npts = ABS(nquad)
istate = 0

INDEX = 0
IF (j == 0) THEN
  qlist(1:100) = 0.0_dp

!        CALL GAUSS IF NQUAD < 0
  IF (nquad < 0) THEN
    itype = 0
    aa = -1.0D0
    bb = 1.0D0
    cc = 0.0D0
    dd = 0.0D0
    ifail = 0
    CALL gauss(itype, aa, bb, cc, dd, npts, weight, abscis, ifail)
    IF (ifail /= 0) THEN
      istate(1) = -4000
      GO TO 30
      
    END IF
    
  END IF
  
END IF

fn_val = 0.0D0

!     SET INTERVAL ENDPOINTS, A AND B
IF (j /= 0) THEN
  a = b
  
ELSE
  a = azero
END IF

IF (rfirst <= azero .OR. j /= 0) THEN
  b = a + hasper
  
ELSE
  b = rfirst
END IF

!     LOOP ON ABSCISSAS FOR INTERVAL #J - STARTS TO CALCULATE QRULE
DO  i = 1, npts
  xi = i
  
!        CALCULATE ABSCISSA Y
  IF (nquad < 0) THEN
    y = (b-a) * abscis(i) / 2.0D0 + (b+a) / 2.0D0
    wt = weight(i)
    
  ELSE
    y = ((xi-1)*b + a*(npts-i)) / (npts-1)
    wt = 1.0D0
  END IF
  
!     IELM IS LOCATION IN SAVPER FOR GPER FUNCTION VALUES
!     J=0: IGNORES.  J=1,2: WHERE TO PUT VALUE.  J>2: WHERE TO GET VALUE FROM

  ielm = npts * MOD(j-1,2) + i
  
  IF (j <= 0 .OR. i /= 1 .OR. nquad <= 0) THEN
    IF (j == 0) THEN
      fun = hfun(y) * gper(y)
      
    ELSE IF (j < 3) THEN
      savper(ielm) = gper(y)

!           CHECK FOR CONSTANT FUNCTION
      IF (savper(ielm) == savper(1)) INDEX = INDEX + 1
      IF (ABS(savper(ielm)) > ABS(savper(ielm-1))) THEN
        gmax = ABS(savper(ielm))
      END IF
      
      fun = hfun(y) * savper(ielm)
      
    ELSE IF (j < 5) THEN

!           CHECK THAT GPER IS PERIODIC WITH PERIOD=PERIOD
      diff = ABS(savper(ielm) - gper(y))
      IF (diff < gmax*1.0D-5) THEN
        fun = hfun(y) * savper(ielm)
        
      ELSE
        istate(1) = -3000
      END IF
      
    ELSE
      IF (INDEX == 2*npts) THEN
        fun = hfun(y)
        
      ELSE
        fun = hfun(y) * savper(ielm)
      END IF
      
    END IF
    
    istate(6) = istate(6) + 1
  END IF
  
  IF (nquad > 0) THEN
    IF (i == 1) THEN
      IF (j == 0) fun = fun / 2.0D0
      IF (j > 0) fun = tendpt
    END IF
    
    IF (i == npts) THEN
      fun = fun / 2.0D0
      tendpt = fun
    END IF
    
    fn_val = fn_val + fun
    
  ELSE
    fn_val = fn_val + wt * fun
  END IF
  
END DO

!     LOOP ON ABSCISSA FOR INTERVAL #J ENDS
IF (INDEX == 2*npts .AND. savper(1) /= 1) THEN
  fn_val = fn_val * savper(1)
END IF

IF (nquad > 0) wsum = npts - 1
IF (nquad < 0) wsum = 2.0D0
fn_val = fn_val * (b-a) / wsum
qlist(j+1) = fn_val

!  N.B. This point can be reached with J = 0
IF (j > 0) THEN
  IF (fn_val*qlist(j) > 0) istate(2) = j
END IF
IF (istate(2) > 9) istate(1) = -200

30 RETURN
END FUNCTION qrule

END MODULE Oscillating_Integration




! **************** BEGIN Driver Program ****************


MODULE oscsub
IMPLICIT NONE
INTEGER, PARAMETER, PRIVATE  :: dp = SELECTED_REAL_KIND(12, 60)

! COMMON /oscsub/ pi, halfpi, ebase, alpha, const6, nix(10), numfun, nb

REAL (dp), SAVE  :: alpha, const6, ebase, halfpi, pi
INTEGER, SAVE    :: nb, nix(10), numfun

END MODULE oscsub



PROGRAM drive1
 
! Code converted using TO_F90 by Alan Miller
! Date: 2002-05-18  Time: 18:21:02
 
!   FILE CONTAINS MAIN, EXACT, HFUN, GPER, G5AND9, RJBESL AND DGAMMA.
!   THE FINAL TWO ARE HIGH QUALITY SOFTWARE WRITTEN BY W.J.CODY.
!   THE FIRST FOUR ARE A MEDIUM QUALITY DRIVER AND SUBROUTINES
!   TO RUN THE SUBMITTED SUBROUTINE OSCINT.
!   THESE FOUR SHARE A LABELLED COMMON LIST.

!      THIS PROGRAM TESTS THE SUBROUTINE, OSCINT, FOR FOUR GIVEN FUNCTIONS
!   (SPECIFIED BY NUMFUN).  THE RESULTS ARE COMPARED TO THE EXACT INTEGRALS
!   FOUND USING ANALYTIC FORMULAS IN THE SUBROUTINE EXACT.

!   CENTRAL LOOP IS ON NUMFUN = 1,4.  THIS IS INSIDE A HAND CODED LOOP,
!   NTIME  = 1,4.  WHEN  NTIME = 1, THE RESULTS ONLY ARE PRINTED.
!   WHEN NTIME = 2, SOME OF THE ABSCISSAS AND FUNCTION VALUES ARE PRINTED,
!   ALONG WITH THE FINITE AVERAGE TABLE.
!   WHEN NTIME = 3 AND 4, MINOR ALTERATIONS ARE MADE IN THE INPUT PARAMETERS
!   WHICH ILLUSTRATE SOME FEATURES OF THE METHOD AND HOW THINGS CAN GO WRONG
!   AND WHETHER OR NOT THE ROUTINE GIVES ANY WARNING.

USE Oscillating_Integration
USE oscsub
IMPLICIT NONE

CHARACTER (LEN=12)  :: gname
CHARACTER (LEN=52)  :: notes(4,4), com1(4,2), com2(4,2), com3(4,2), hname, m52

! EXTERNAL hfun, gper, g5and9

INTERFACE
  SUBROUTINE g5and9(ITYPE, A, B, C, D, N, WEIGHT, ABSCIS, IFAIL)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    INTEGER, INTENT(IN)     :: itype
    REAL (dp), INTENT(IN)   :: a, b, c, d
    INTEGER, INTENT(IN)     :: n
    REAL (dp), INTENT(OUT)  :: weight(n), abscis(n)
    INTEGER, INTENT(OUT)    :: ifail
  END SUBROUTINE g5and9

  FUNCTION hfun(x) RESULT(fn_val)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp), INTENT(IN)  :: x
    REAL (dp)              :: fn_val
  END FUNCTION hfun

  FUNCTION gper(x) RESULT(fn_val)
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp), INTENT(IN)  :: x
    REAL (dp)              :: fn_val
  END FUNCTION gper

  FUNCTION exact() RESULT(fn_val)
    USE oscsub
    IMPLICIT NONE
    INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)
    REAL (dp)  :: fn_val
  END FUNCTION exact
END INTERFACE

REAL (dp)  :: azero, cin, eps, period, qlist(100), result, rfirst
REAL (dp)  :: savper(128), weight(18), abscis(18)
INTEGER    :: i, j, istate(6), k, ndim1, ndim2, nquad, nrun, ncol,  &
              nptop, nterms, ntime, ntm2

! COMMON /oscsub/ pi, halfpi, ebase, alpha, const6, nix(10), numfun, nb

!     SET UP THE NOTES. THESE APPEAR BEFORE EACH RUN.
notes(1,1) = ' FIRST TIME. BRIEF OUTPUT. NOTE EPS AND DIFFERENCE  '
notes(1,2) = ' SECOND TIME. SAME AS FIRST BUT WITH DETAILED OUTPUT'
notes(1,3) = ' NOW NDIM1 = 10. SPECIAL VALUE FOR SPACE ECONOMY.   '
notes(1,4) = ' PERIOD WRONG. IT SHOULD BE TWOPI = 0.628...+01     '
DO  k = 1, 4
  notes(2:4,k) = notes(1,k)
END DO

notes(2,3) = ' NOW NQUAD = -9 FOR 9 POINT GAUSS QUADRATURE RULE   '
notes(3,3) = ' NOW NDIM1 = 5, TO ILLUSTRATE NO CONVERGENCE.       '
notes(4,3) = ' NOW NDIM2 = 11, TO ILLUSTRATE VARIANT BEHAVIOR     '

!     SET UP THE COMMENTS. THESE APPEAR AFTER EACH RUN.
m52 = ' MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM '
com3(1:4, 1:2) = m52

com1(1,2) = ' NOTE ISTATE(1), ISTATE(6).  NEGATIVE PERIOD NOTICED'
com2(1,2) = ' BEFORE ANY SERIOUS COMPUTATION.                    '
com1(2,2) = ' WRONG PERIOD NOTICED QUICKLY SINCE GPER = COS(X)   '
com2(2,2) = ' WAS FOUND NOT TO HAVE CLAIMED PERIOD.              '
com1(3,2) = ' THIS EASY CALCULATION ENDED DURING THE SIGN CHANGE '
com2(3,2) = ' GRACE PERIOD. (YOU CANT LOOSE EM ALL.)             '
com1(4,2) = ' SINCE GPER = 1, WRONG PERIOD WAS NOT NOTICED, BUT  '
com2(4,2) = ' SIGN CHANGE PATTERN ULTIMATELY LED TO TERMINATION  '
com3(4,2) = ' AFTER THE GRACE PERIOD.    MMMMMMMMMMMMMMMMMMMMMM  '

com1(1,1) = ' NOTE ONLY SMALL ARRAY NEEDED. THE READER MAY       '
com2(1,1) = ' IDENTIFY THIS SUBSET OF THE PREVIOUS NUMFUN=1 TABLE'
com1(2,1) = ' THIS QUADRATURE RULE INADEQUATE. NOTE THE STEADY   '
com2(2,1) = ' CONVERGENCE OF THE TABLE ELEMENTS BELOW TO A RESULT'
com3(2,1) = ' WHICH IS IN FACT WRONG. ROUTINE CANNOT KNOW THIS.  '
com1(3,1) = ' BECAUSE NDIM1 = 5, BUT THE WORK ARRAY HAS DIMENSION'
com2(3,1) = ' 100, THE ELEMENTS APPEAR IN UNFAMILIAR LOCATIONS.  '
com1(4,1) = ' THE PREVIOUSLY CHOSEN ELEMENT (16,14) UNREACHABLE. '
com2(4,1) = ' EQUALLY GOOD, BUT MARGINALLY MORE EXPENSIVE        '
com3(4,1) = ' ELEMENT (25,11) IS CHOSEN INSTEAD.     MMMMMMMMMMM '

!      CONSTANTS, ETC.USED IN SUBROUTINES.
ebase = EXP(1.0D0)
pi = 4.0D0 * ATAN(1.0D0)
halfpi = pi / 2.0D0
cin = 1.0D0 + SQRT(2.0D0)
const6 = 1.0D0 / LOG(cin)
alpha = 0.0D0
nb = 2

ntime = 1
!   HAND CODED LOOP ON NTIME = 1,2,3,4  STARTS HERE.

!   NOW, SET THE CODE-CHECK OUTPUT PARAMETERS.
!   SET VALUES OF NIX TO PRINT VARIOUS CODE CHECKS
!       NIX(1) > 0    CAUSES VALUE RETURNED FROM XINT TO BE PRINTED
!                     IN THE MAIN ROUTINE FOR THE FIRST N CALLS
!       NIX(2) > 0    CAUSES FIRST NIX(2) ROWS OF THE FORWARD AVERAGE
!                     TABLE TO BE PRINTED.
!       NIX(3) > 0    CAUSES OUTPUT FROM FUN TO BE PRINTED FOR THE
!                     FIRST N CALLS

50 nix(1) = 0
nix(3) = 0
IF (ntime > 1) nix(2) = 40


DO  numfun = 1, 4
  
  nrun = 10 * ntime + numfun
  IF (ntime == 2) nix(1) = 12
  IF (ntime == 2) nix(3) = 12
  
!      FIRST, CLEAR VARIOUS WORK SPACES.  THIS IS UNNECESSARY FOR RUNNING,
!      BUT IS USEFUL FOR TIDY OUTPUT.
  
  qlist(1:100) = 0.0D0
  result = 0.0D0
  
!     SECOND, SET THE INPUT PARAMETERS FOR OSCINT.
  
!     AZERO IS THE LOWER LIMIT OF INTEGRATION
  azero = 0.0D0
  
  period = 2.0D0 * pi
  IF (numfun == 6) period = pi
  
!     RFIRST IS THE RIGHT-HAND ENDPOINT OF THE FIRST INTERVAL
  rfirst = 0.0D0
  
!     NQUAD REPRESENTS THE TYPE OF QUADRATURE RULE USED
!           NQUAD = -5    5 PT GAUSS RULE
!           NQUAD = -9    9 PT GAUSS RULE
!           NQUAD = N,N>0 (N ODD) N POINT TRAPEZOIDAL RULE(N-1 PANELS)
  nquad = -9
  IF (numfun == 2) nquad = 64
  
!     SET EPSILON, THE REQUESTED TOLERANCE
  eps = 1.0D-13
  
!     NDIM1 AND NDIM2 ARE THE DIMENSIONS OF THE WORK TABLE.  THEY MUST BE
!     <= THE DIMENSIONS GIVEN TO WORK IN THE DIMENSION STATEMENT
  ndim1 = 100
  ndim2 = 14
  
!     WHEN NTIME = 1  ONLY PRINCIPALRESULTS
!     WHEN NTIME = 2  FULL FINITE AVERAGE TABLE AND  OTHER CODE CHECK.
!     WHEN NTIME = 3 OR 4 INPUT CHANGES (MISTAKES,ETC.)
  
  IF (ntime == 3) THEN
    IF (numfun == 1) ndim1 = 10
    IF (numfun == 2) nquad = -9
    IF (numfun == 3) ndim1 = 5
    IF (numfun == 4) ndim2 = 11
  END IF
  
  IF (ntime >= 4) THEN
    IF (numfun == 1) period = -pi
    IF (numfun == 2) period = pi
    IF (numfun == 3) period = pi
    IF (numfun == 4) period = pi
  END IF
  
!      NOW, PRINT OUT THE INPUT PARAMETERS BEFORE THE CALL TO OSCINT.
  
  gname = ' 1.0D0      '
  IF (numfun == 2) gname = ' COS(X)     '
  IF (numfun == 6) gname = ' C4(X) -3/8 '
  IF (numfun == 1) hname = 'SIN(X)/X'
  IF (numfun == 2) hname = '1.0D0/(1+X**2)'
  IF (numfun == 3) hname = 'EXP(-X*SINH(PI/2)*J1(X)'
  IF (numfun == 4) hname =  &
                   'CONST(1 - EXP(X)) * J0(X) / X.  (QUADPACK, PAGE 118)'
  IF (numfun == 5) hname = '(1 + 100*EXP(-X**2/PI**2))*J1(X)'
  IF (numfun == 6) hname = '(COS**4(X) - 3/8)/(1 + X**2)'
  
  WRITE(*, 5400)
  WRITE(*, 5000) numfun, gname, hname
  WRITE(*, 5100) notes(numfun,ntime), m52
  WRITE(*, 5200) azero, period, rfirst, eps, nquad
  WRITE(*, 5300) 'G5AND9', ndim1, ndim2
  WRITE(*, 5400)
  IF (nix(1) > 0 .OR. nix(3) > 0) WRITE(*, 5500)
  
  
  IF (nrun /= 31) THEN
    CALL oscint(azero, period, rfirst, eps, nquad, ndim1, ndim2, g5and9,  &
                hfun, gper, savper, weight, abscis, qlist, result, istate)
  ELSE
    
    CALL oscint(azero, period, rfirst, eps, nquad, ndim1, ndim2, g5and9,  &
                hfun, gper, savper, weight, abscis, qlist, result, istate)
  END IF
  
!      NOW, PRINT OUT THE RESULTS.
  
  WRITE(*, 5600)
  WRITE(*, 5700) result, exact(), exact() - result
  WRITE(*, 5800) (istate(i),i=1,5)
  IF (ntime <= 2) WRITE(*, 5900) istate(6)
  ntm2 = ntime - 2
  IF (ntime >= 3) WRITE(*, 6000) com1(numfun,ntm2),  &
                  com2(numfun,ntm2), istate(6), com3(numfun,ntm2)
  WRITE(*, 5000) numfun, gname, hname
  WRITE(*, 5400)
  
  IF (nix(2) > 0) THEN
    nterms = istate(3)
    ncol = istate(4)
    nptop = MIN(nterms,nix(2))
    IF (nrun /= 31) THEN
      
      IF (nrun == 33) nptop = 25
    ELSE
      
      nptop = MIN(10,nix(2))
      nptop = MIN(nterms,nptop)
    END IF
    
    WRITE(*, 5000) numfun, gname, hname
    WRITE(*, 5400)
  END IF
  
  WRITE(*, 6300)
END DO

ntime = ntime + 1
IF (ntime < 5) GO TO 50

5000 FORMAT (/' NUMFUN =  ',i1, '     F(X) = G(X).H(X)     G(X) = ',  &
             a12, '  H(X) = ', a52)
5100 FORMAT (/' INPUT PARAMETERS FOR OSCINT-', t40, a52/ t40, a52/)
5200 FORMAT (t5, 'AZERO', t22, 'PERIOD', t40, 'RFIRST', t58, 'EPS', t73, 'NQUAD'/ &
             g13.6, t19, g12.6, t37, g12.6, t55, g12.6, t73, i3/)
5300 FORMAT (/t5, '(GAUSS)          (HFUN)            (GPER)            NDIM1', &
             '          NDIM2'/ t5, a6, t59, i3, t73, i3/)
5400 FORMAT ('---------------------------------------------------------',  &
             '-----------------------')
5500 FORMAT (/'    CODE CHECK OUTPUT FROM HFUN, GPER, QRULE'/)
5600 FORMAT (/' PRINCIPAL OUTPUT PARAMETERS-'//)
5700 FORMAT (t6, 'RESULT', t32, 'EXACT', t54, 'DIFFERENCE'/   &
             g17.8, t28, g16.8, t52, g16.8//)
5800 FORMAT (t5, 'ISTATE(1)     ISTATE(2)     ISTATE(3)     ISTATE(4)',  &
             '       ISTATE(5)'/  &
             t5, '(IFAIL)       (LSIGCH)        (N)       (COLUMN NO.)', &
             '      (ROW NO.)'/   &
             i10, t22, i3, t35, i3, t48, i3, t62, i4//)

5900 FORMAT (t5, 'ISTATE(6)'/ ' NO. OF FUNCTION VALUES'/ t5, i4//)
6000 FORMAT (t5, 'ISTATE(6)', t40, a52/ ' NO. OF FUNCTION VALUES', t40, a52/  &
             t5, i4, t40, a52//)
6100 FORMAT (i4, 10g12.4/)
6200 FORMAT (i4, 14g9.2/)
6300 FORMAT (///)
END PROGRAM drive1



FUNCTION exact() RESULT(fn_val)

!      EXACT IS THE VALUE OF THE INTEGRAL OF F(X) OVER (0,INFINITY).
!      HERE, OF COURSE, F(X) = H(X).G(X) GIVEN BY HFUN AND GPER BELOW.

USE oscsub
IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)

REAL (dp)  :: fn_val

! COMMON /oscsub/ pi, halfpi, ebase, alpha, const6, nix(10), numfun, nb

fn_val = 0.0D0
IF (numfun == 1) fn_val = halfpi
IF (numfun == 2) fn_val = pi / (2.0D0*ebase)
IF (numfun == 3) THEN
  fn_val = EXP(-(1.0D0 + alpha)*halfpi) / COSH(halfpi)
END IF

IF (numfun == 4) fn_val = 1.0D0
IF (numfun == 6) THEN
  fn_val = pi / 16.0D0 * (ebase**(-4) + 4.0D0*ebase**(-2))
END IF

RETURN
END FUNCTION exact



FUNCTION hfun(x) RESULT(fn_val)

USE oscsub
IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)

REAL (dp), INTENT(IN)  :: x
REAL (dp)              :: fn_val

! EXTERNAL rjbesl

! COMMON /oscsub/ pi, halfpi, ebase, alpha, const6, nix(10), numfun, nb
REAL (dp)  :: b(100), f1, sinchu, u
INTEGER    :: ncalc

!      ALPHA - FRACTIONAL PART OF ORDER FOR WHICH BESSEL FUNCTION IS TO
!              BE CALCULATED, 0 <= ALPHA < 1
!      NB      NUMBER OF BESSEL FUNCTIONS IN SEQUENCE.

!CCC      IF (NUMFUN .EQ. 1) F(X) = SIN(X)/X
!CCC      IF (NUMFUN .EQ. 2) F(X) = COS(X)/(1+X**2)
!CCC      IF (NUMFUN .EQ. 3) F(X) = EXP(-X*SINH(PI/2)*J1(X)
!CCC      IF (NUMFUN .EQ. 4) F(X) = QUADPACK 5.2.3. PAGE118
!CCC      IF (NUMFUN .EQ. 5) F(X) = (1 + 100*EXP(-X**2/PI**2))*J1(X)
!CCC      IF (NUMFUN .EQ. 6) F(X) = (COS**4(X) - 3/8)/(1 + X**2)

!CCC      WHEN NUMFUN IS 1,3,4 OR 5, H(X) = F(X).
!CCC      WHEN NUMFUN IS 2 OR 6,     H(X) = 1/(1 + X**2)

IF (numfun == 1) THEN
  fn_val = 1.0D0 - x ** 2 / 6.0D0 + x ** 4 / 120.0D0
  IF (ABS(x) > 10.0D-4) fn_val = SIN(x) / x
ELSE
  
  IF (numfun == 2) fn_val = 1.0D0 / (1.0D0 + x**2)
  IF (numfun < 6) THEN
    IF (numfun >= 3) CALL rjbesl(x, alpha, nb, b, ncalc)
    IF (numfun == 5) fn_val = 1.0D0 + 100.0_dp*EXP(-(x/pi)**2) * b(2)
    
    IF (numfun == 3) THEN
      u = halfpi
      sinchu = (ebase**u - ebase**(-u)) / 2.0D0
      fn_val = ebase ** (-x*sinchu) * b(2)
    END IF
    
    IF (numfun == 4) THEN
      IF (x < 10.0D-8) f1 = 1.0D0
      IF (x >= 10.0D-8) f1 = (1.0D0 - EXP(-x)) / x
      fn_val = f1 * const6 * b(1)
    END IF
  END IF
  
  IF (numfun == 6) fn_val = 1.0D0 / (1.0D0 + x**2)
END IF

IF (nix(3) > 0) THEN
  nix(3) = nix(3) - 1
  WRITE(*, 5000) x, fn_val
END IF

RETURN

5000 FORMAT ('   X = ', g22.16,'   HFUN = ', g22.16)
END FUNCTION hfun




FUNCTION gper(x) RESULT(fn_val)

USE oscsub
IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)

REAL (dp), INTENT(IN)  :: x
REAL (dp)              :: fn_val

! COMMON /oscsub/ pi, halfpi, ebase, alpha, const6, nix(10), numfun, nb

fn_val = 1.0D0
IF (numfun == 2) fn_val = COS(x)
IF (numfun == 6) fn_val = COS(x) ** 4 - 0.375_dp
IF (nix(3) /= 0) WRITE(*, 5000) x, fn_val
RETURN

5000 FORMAT ('   X = ', g22.16, '   GPER = ', g22.16)
END FUNCTION gper



SUBROUTINE g5and9(itype, a, b, c, d, nquad, weight, abscis, ierr)

!   THIS IS AN EXTRACT FROM A QUADRATURE ROUTINE CONSTRUCTED ONLY FOR
!   USE IN A DRIVER WHICH ILLUSTRATES OSCINT.  A NAG LIBRARY
!   SUBSCRIBER MAY REPLACE THIS BY D01BCF FOR GENERAL USE.

IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)

INTEGER, INTENT(IN)     :: itype
REAL (dp), INTENT(IN)   :: a
REAL (dp), INTENT(IN)   :: b
REAL (dp), INTENT(IN)   :: c
REAL (dp), INTENT(IN)   :: d
INTEGER, INTENT(IN)     :: nquad
REAL (dp), INTENT(OUT)  :: weight(nquad)
REAL (dp), INTENT(OUT)  :: abscis(nquad)
INTEGER, INTENT(OUT)    :: ierr

INTEGER    :: i

!      THE FOLLOWING DATA ARE WEIGHTS AND ABSCISSAS OF THE FIVE POINT
!      AND THE NINE POINT GAUSS-LEGENDRE QUADRATURE RULES RESPECTIVELY.
!      NORMALISED TO THE INTERVAL (-1,1).
REAL (dp), PARAMETER  :: wt5(5)  = (/   &
           .2369268850561891_dp, .4786286704993665_dp, .5688888888888889_dp,  &
           .4786286704993665_dp, .2369268850561891_dp /)
REAL (dp), PARAMETER  :: absc5(5) = (/    &
          -.9061798459386640_dp, -.5384693101056831_dp, 0.0_dp,  &
           .5384693101056830_dp, .9061798459386640_dp /)
REAL (dp), PARAMETER  :: wt9(9) = (/     &
           .0812743883615745_dp, .1806481606948574_dp, .2606106964029355_dp, &
           .3123470770400029_dp, .3302393550012598_dp, .3123470770400029_dp, &
           .2606106964029356_dp, .1806481606948575_dp, .8127438836157467D-01 /)
REAL (dp), PARAMETER  :: absc9(9) = (/    &
          -.9681602395076261_dp, -.8360311073266358_dp, -.6133714327005904_dp, &
          -.3242534234038090_dp, 0.0_dp, .3242534234038087_dp,    &
           .6133714327005902_dp, .8360311073266357_dp, .9681602395076260_dp /)

ierr = 59

IF (nquad == 5) THEN
  ierr = 0
  DO  i = 1, 5
    abscis(i) = absc5(i)
    weight(i) = wt5(i)
  END DO
END IF

IF (nquad == 9) THEN
  ierr = 0
  DO  i = 1, 9
    abscis(i) = absc9(i)
    weight(i) = wt9(i)
  END DO
END IF

RETURN
END SUBROUTINE g5and9



SUBROUTINE rjbesl(x, alpha, nb, b, ncalc)
!-----------------------------------------------------------

!  THIS ROUTINE CALCULATES BESSEL FUNCTIONS J SUB(N+ALPHA) (X)
!  FOR NON-NEGATIVE ARGUMENT X, AND NON-NEGATIVE ORDER N+ALPHA.


! EXPLANATION OF VARIABLES IN THE CALLING SEQUENCE

! X     - WORKING PRECISION NON-NEGATIVE REAL ARGUMENT FOR WHICH
!         J'S ARE TO BE CALCULATED.
! ALPHA - WORKING PRECISION FRACTIONAL PART OF ORDER FOR WHICH
!         J'S OR EXPONENTIALLY SCALED J'S (J*EXP(X)) ARE
!         TO BE CALCULATED.  0 .LE. ALPHA < 1.0.
! NB    - INTEGER NUMBER OF FUNCTIONS TO BE CALCULATED, NB > 0.
!         THE FIRST FUNCTION CALCULATED IS OF ORDER ALPHA, AND THE
!         LAST IS OF ORDER (NB - 1 + ALPHA).
! B     - WORKING PRECISION OUTPUT VECTOR OF LENGTH NB.  IF THE ROUTINE
!         TERMINATES NORMALLY (NCALC=NB), THE VECTOR B CONTAINS THE
!         FUNCTIONS J/ALPHA/(X) THROUGH J/NB-1+ALPHA/(X), OR THE
!         CORRESPONDING EXPONENTIALLY SCALED FUNCTIONS.
! NCALC - INTEGER OUTPUT VARIABLE INDICATING POSSIBLE ERRORS.
!         BEFORE USING THE VECTOR B, THE USER SHOULD CHECK THAT
!         NCALC=NB, I.E., ALL ORDERS HAVE BEEN CALCULATED TO
!         THE DESIRED ACCURACY.  SEE ERROR RETURNS BELOW.


! EXPLANATION OF MACHINE-DEPENDENT CONSTANTS

!   IN THE FOLLOWING DISCUSSION, THE DESIRED DECIMAL SIGNIFICANCE
!   IS DENOTED BY  NSIG = IFIX(ALOG10(2)*NBIT+1), WHERE NBIT IS
!   THE NUMBER OF BITS IN THE MANTISSA OF A WORKING PRECISION
!   VARIABLE.  (LOWER VALUES OF NSIG RESULT IN DECREASED ACCURACY
!   WHILE HIGHER VALUES INCREASE CPU TIME WITHOUT INCREASING
!   ACCURACY.) RELATIVE TRUNCATION ERROR IS THEN LIMITED TO
!   T = 0.5*10**(-NSIG).  THE FOLLOWING CONSTANTS ARE USED IN
!   THIS PROGRAM:

! ENTEN  - 10.0 ** K, WHERE K IS THE LARGEST INTEGER SUCH THAT
!          ENTEN IS MACHINE-REPRESENTABLE IN WORKING PRECISION.
! ENSIG  - 10.0 ** NSIG.
! RTNSIG - 10.0 ** (-K) FOR THE SMALLEST INTEGER K SUCH THAT
!          K .GE. NSIG/4.
! ENMTEN - THE SMALLEST ABS(X) SUCH THAT X/4 DOES NOT UNDERFLOW.
! XLARGE - UPPER LIMIT ON THE MAGNITUDE OF X.  BEAR IN MIND
!          THAT IF ABS(X)=N, THEN AT LEAST N ITERATIONS OF THE
!          BACKWARD RECURSION WILL BE EXECUTED.

!     APPROXIMATE VALUES FOR SOME IMPORTANT MACHINES ARE:

!         IBM/195    CDC/7600  UNIVAC/1108   VAX 11/780 (UNIX)
!          (D.P.)  (S.P.,RNDG)    (D.P.)     (S.P.)     (D.P.)

! NSIG      16         14          18           8        17
! ENTEN   1.0D75     1.0E322     1.0D307     1.0E38    1.0D38
! ENSIG   1.0D16     1.0E14      1.0D18      1.0E8     1.0D17
! RTNSIG  1.0D-4     1.0E-4      1.0D-5      1.0E-2    1.0D-4
! ENMTEN  2.2D-78    1.0E-290    1.2D-308    1.2E-37   1.2D-37
! XLARGE  1.0D4      1.0E4       1.0D4       1.0E4     1.0D4


! ERROR RETURNS

!  IN CASE OF AN ERROR,  NCALC .NE. NB,  AND NOT ALL J'S ARE
!  CALCULATED TO THE DESIRED ACCURACY.

!  NCALC < 0:  AN ARGUMENT IS OUT OF RANGE. FOR EXAMPLE,
!     NB .LE. 0, ALPHA < 0 OR > 1, OR X IS TOO LARGE.
!     IN THIS CASE, THE B-VECTOR IS NOT CALCULATED, AND NCALC IS
!     SET TO  MIN0(NB,0)-1  SO THAT NCALC .NE. NB.

!  NB > NCALC > 0: NOT ALL REQUESTED FUNCTION VALUES COULD
!     BE CALCULATED ACCURATELY.  THIS USUALLY OCCURS BECAUSE NB IS
!     MUCH LARGER THAN ABS(X).  IN THIS CASE, B(N) IS CALCULATED
!     TO THE DESIRED ACCURACY FOR  N .LE. NCALC,  BUT PRECISION
!     IS LOST FOR NCALC < N .LE. NB.  IF B(N) DOES NOT VANISH
!     FOR  N > NCALC  (BECAUSE IT IS TOO SMALL TO BE REPRESENTED),
!     AND  B(N)/B(NCALC) = 10**(-K), THEN ONLY THE FIRST NSIG-K
!     SIGNIFICANT FIGURES OF B(N) CAN BE TRUSTED.

! OTHER SUBPROGRAMS REQUIRED (SINGLE PRECISION VERSION)

!     ABS,AINT,AMAX1,COS,GAMMA,SIN,SQRT,FLOAT,IFIX,MIN0

! OTHER SUBPROGRAMS REQUIRED (DOUBLE PRECISION VERSION)

!     DABS,DBLE,DCOS,DINT,DGAMMA,DMAX1,DSIN,DSQRT,FLOAT,
!          IFIX,MIN0,SNGL


! ACKNOWLEDGEMENT

!  THIS PROGRAM IS BASED ON A PROGRAM WRITTEN BY DAVID J. SOOKNE
!  THAT COMPUTES VALUES OF THE BESSEL FUNCTIONS J OR I OF REAL
!  ARGUMENT AND INTEGER ORDER.  MODIFICATIONS INCLUDE THE
!  RESTRICTION OF THE COMPUTATION TO THE J BESSEL FUNCTION OF
!  NON-NEGATIVE REAL ARGUMENT, THE EXTENSION OF THE COMPUTATION TO
!  ARBITRARY POSITIVE ORDER, AND THE ELIMINATION OF MOST UNDERFLOW.


!      MODIFIED BY: W. J. CODY
!                   ARGONNE NATIONAL LABORATORY

!      LATEST MODIFICATION: AUGUST 4, 1982

!-----------------------------------------------------------

IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)

REAL (dp), INTENT(IN)   :: x
REAL (dp), INTENT(IN)   :: alpha
INTEGER, INTENT(IN)     :: nb
REAL (dp), INTENT(OUT)  :: b(nb)
INTEGER, INTENT(OUT)    :: ncalc

INTEGER    :: i, j, k, l, m, magx, n, nbmx, nend, nstart
REAL (dp)  :: alpem, alp2em, capp, capq, dgamma,  &
    eighth, em, en, enmten, ensig, enten, fact, four, gnu, half,  &
    halfx, one, p, pi2, plast, pold, psave, psavel, rtnsig, s,  &
    sum, t, t1, tempa, tempb, tempc, test, three, tover, two,  &
    twofiv, twopi1, twopi2, xc, xin, xk, xlarge, xm, vcos, vsin, z, zero
DIMENSION  fact(25)
!-----------------------------------------------------------
!  MATHEMATICAL CONSTANTS

!   PI2    - 2 / PI
!   TWOPI1 - FIRST FEW SIGNIFICANT DIGITS OF 2 * PI
!   TWOPI2 - (2*PI - TWOPI) TO WORKING PRECISION, I.E.,
!            TWOPI1 + TWOPI2 = 2 * PI TO EXTRA PRECISION
!-----------------------------------------------------------
DATA pi2, twopi1, twopi2 /0.636619772367581343075535_dp, 6.28125_dp,  &
    1.935307179586476925286767D-3/
DATA zero, eighth, half, one /0.0_dp, 0.125_dp, 0.5_dp, 1.0_dp/
DATA two, three, four, twofiv /2.0_dp, 3.0_dp, 4.0_dp, 25.0_dp/
!-----------------------------------------------------------
!  MACHINE DEPENDENT PARAMETERS
!-----------------------------------------------------------
DATA enten, ensig, rtnsig /1.0D38, 1.0D17, 1.0D-4/
DATA enmten, xlarge /1.2D-37, 1.0D4/
!---------------------------------------------------------------------
!     FACTORIAL(N)
!---------------------------------------------------------------------
DATA fact /1.0D0, 1.0D0, 2.0D0, 6.0D0, 24.0D0, 1.2D2, 7.2D2,  &
    5.04D3, 4.032D4, 3.6288D5, 3.6288D6, 3.99168D7, 4.790016D8,  &
    6.2270208D9, 8.71782912D10, 1.307674368D12, 2.0922789888D13,  &
    3.55687428096D14, 6.402373705728D15, 1.21645100408832D17,  &
    2.43290200817664D18, 5.109094217170944D19,  &
    1.12400072777760768D21, 2.585201673888497664D22, 6.2044840173323943936D23/
!---------------------------------------------------------------------
magx = x
IF (.NOT.(nb > 0 .AND. x >= zero .AND. x <= xlarge .AND. alpha >= zero  &
          .AND. alpha < one)) THEN
!-----------------------------------------------------------
! ERROR RETURN -- X,NB,OR ALPHA IS OUT OF RANGE
!-----------------------------------------------------------
  ncalc = MIN(nb,0) - 1
ELSE
!-----------------------------------------------------------
! INITIALIZE RESULT ARRAY TO ZERO
!-----------------------------------------------------------
  ncalc = nb
  DO  i = 1, nb
    b(i) = zero
  END DO
!-----------------------------------------------------------
! BRANCH TO USE 2-TERM ASCENDING SERIES FOR SMALL X,
! AND ASYMPTOTIC FORM FOR LARGE X WHEN NB IS NOT TOO LARGE
!-----------------------------------------------------------
  IF (x >= rtnsig) THEN
    IF ((x > twofiv).AND.(nb <= magx+1)) GO TO 170
!-----------------------------------------------------------
! USE RECURRENCE TO GENERATE RESULTS.
! FIRST INITIALIZE THE CALCULATION OF P*S
!-----------------------------------------------------------
    nbmx = nb - magx
    n = magx + 1
    en = 2*(n + alpha)
    plast = one
    p = en / x
!-----------------------------------------------------------
! CALCULATE GENERAL SIGNIFICANCE TEST
!-----------------------------------------------------------
    test = ensig + ensig
    IF (nbmx >= 3) THEN
!-----------------------------------------------------------
! CALCULATE P*S UNTIL N = NB-1.  CHECK FOR POSSIBLE OVERFLOW.
!-----------------------------------------------------------
      tover = enten / ensig
      nstart = magx + 2
      nend = nb - 1
      en = nstart + nstart - two + (alpha + alpha)
      DO  n = nstart, nend
        en = en + two
        pold = plast
        plast = p
        p = en * plast / x - pold
        IF (p > tover) GO TO 40
      END DO
      n = nend
      en = 2*(n + alpha)
!-----------------------------------------------------------
! CALCULATE SPECIAL SIGNIFICANCE TEST FOR NBMX.GT.2.
!-----------------------------------------------------------
      test = MAX(test,DSQRT(plast*ensig)*DSQRT(p+p))
    END IF
!-----------------------------------------------------------
! CALCULATE P*S UNTIL SIGNIFICANCE TEST PASSES
!-----------------------------------------------------------
    30 n = n + 1
    en = en + two
    pold = plast
    plast = p
    p = en * plast / x - pold
    IF (p < test) GO TO 30
    GO TO 80
!-----------------------------------------------------------
! TO AVOID OVERFLOW, DIVIDE P*S BY TOVER.  CALCULATE P*S
! UNTIL ABS(P) > 1.
!-----------------------------------------------------------
    40     tover = enten
    p = p / tover
    plast = plast / tover
    psave = p
    psavel = plast
    nstart = n + 1
    50     n = n + 1
    en = en + two
    pold = plast
    plast = p
    p = en * plast / x - pold
    IF (p <= one) GO TO 50
    tempb = en / x
!-----------------------------------------------------------
! CALCULATE BACKWARD TEST, AND FIND NCALC, THE HIGHEST N
! SUCH THAT THE TEST IS PASSED.
!-----------------------------------------------------------
    test = pold * plast * (half-half/(tempb*tempb)) / ensig
    p = plast * tover
    n = n - 1
    en = en - two
    nend = MIN0(nb,n)
    DO  l = nstart, nend
      ncalc = l
      pold = psavel
      psavel = psave
      psave = en * psavel / x - pold
      IF (psave*psavel > test) GO TO 70
    END DO
    ncalc = nend + 1
    70 ncalc = ncalc - 1
!-----------------------------------------------------------
! INITIALIZE THE BACKWARD RECURSION AND THE NORMALIZATION SUM
!-----------------------------------------------------------
    80 n = n + 1
    en = en + two
    tempb = zero
    tempa = one / p
    m = 2 * n - 4 * (n/2)
    sum = zero
    em = n/2
    alpem = (em-one) + alpha
    alp2em = (em+em) + alpha
    IF (m /= 0) sum = tempa * alpem * alp2em / em
    nend = n - nb
    IF (nend > 0) THEN
!     IF (NEND) 140, 120, 100
!-----------------------------------------------------------
! RECUR BACKWARD VIA DIFFERENCE EQUATION, CALCULATING (BUT
! NOT STORING) B(N), UNTIL N = NB.
!-----------------------------------------------------------
      DO  l = 1, nend
        n = n - 1
        en = en - two
        tempc = tempb
        tempb = tempa
        tempa = (en*tempb) / x - tempc
        m = 2 - m
        IF (m /= 0) THEN
          em = em - one
          alp2em = (em+em) + alpha
          IF (n == 1) GO TO 100
          alpem = (em-one) + alpha
          IF (alpem == zero) alpem = one
          sum = (sum+tempa*alp2em) * alpem / em
        END IF
      END DO
    END IF
!-----------------------------------------------------------
! STORE B(NB)
!-----------------------------------------------------------
    100     b(n) = tempa
    IF (nend < 0) THEN
      nend = -nend
    ELSE
      
      IF (nb <= 1) THEN
        alp2em = alpha
        IF ((alpha+one) == one) alp2em = one
        sum = sum + b(1) * alp2em
        GO TO 130
      END IF
!-----------------------------------------------------------
! CALCULATE AND STORE B(NB-1)
!-----------------------------------------------------------
      n = n - 1
      en = en - two
      b(n) = (en*tempa) / x - tempb
      IF (n == 1) GO TO 120
      m = 2 - m
      IF (m /= 0) THEN
        em = em - one
        alp2em = (em+em) + alpha
        alpem = (em-one) + alpha
        IF (alpem == zero) alpem = one
        sum = (sum+b(n)*alp2em) * alpem / em
      END IF
    END IF
!     GO TO 160
!-----------------------------------------------------------
! N.LT.NB, SO STORE B(N) AND SET HIGHER ORDERS TO ZERO
!-----------------------------------------------------------
! 140 B(N) = TEMPA
!     NEND = -NEND
!     DO 150 L=1,NEND
!       B(N+L) = ZERO
! 150 CONTINUE
    nend = n - 2
    IF (nend /= 0) THEN
!-----------------------------------------------------------
! CALCULATE VIA DIFFERENCE EQUATION AND STORE B(N),
! UNTIL N = 2
!-----------------------------------------------------------
      DO  l = 1, nend
        n = n - 1
        en = en - two
        b(n) = (en*b(n+1)) / x - b(n+2)
        m = 2 - m
        IF (m /= 0) THEN
          em = em - one
          alp2em = (em+em) + alpha
          alpem = (em-one) + alpha
          IF (alpem == zero) alpem = one
          sum = (sum + b(n)*alp2em) * alpem / em
        END IF
      END DO
    END IF
!-----------------------------------------------------------
! CALCULATE B(1)
!-----------------------------------------------------------
    b(1) = two * (alpha+one) * b(2) / x - b(3)
    120 em = em - one
    alp2em = (em+em) + alpha
    IF (alp2em == zero) alp2em = one
    sum = sum + b(1) * alp2em
!-----------------------------------------------------------
! NORMALIZE.  DIVIDE ALL B(N) BY SUM.
!-----------------------------------------------------------
    130 IF ((alpha+one) /= one) sum = sum * dgamma(alpha) * (x*half) ** (-alpha)
    tempa = enmten
    IF (sum > one) tempa = tempa * sum
    DO  n = 1, nb
      IF (ABS(b(n)) < tempa) b(n) = zero
      b(n) = b(n) / sum
    END DO
    GO TO 210
  END IF
!-----------------------------------------------------------
! TWO-TERM ASCENDING SERIES FOR SMALL X
!-----------------------------------------------------------
  tempa = one
  alpem = one + alpha
  halfx = zero
  IF (x > enmten) halfx = half * x
  IF (alpha /= zero) tempa = halfx ** alpha / (alpha* dgamma(alpha))
  tempb = zero
  IF ((x+one) > one) tempb = -halfx * halfx
  b(1) = tempa + tempa * tempb / alpem
  IF (x /= zero .AND. b(1) == zero) ncalc = 0
  IF (nb == 1) GO TO 210
  IF (x <= zero) THEN
    DO  n = 2, nb
      b(n) = zero
    END DO
    GO TO 210
  END IF
!-----------------------------------------------------------
! CALCULATE HIGHER ORDER FUNCTIONS
!-----------------------------------------------------------
  tempc = halfx
  tover = (enmten+enmten) / x
  IF (tempb /= zero) tover = enmten / tempb
  DO  n = 2, nb
    tempa = tempa / alpem
    alpem = alpem + one
    tempa = tempa * tempc
    IF (tempa <= tover*alpem) tempa = zero
    b(n) = tempa + tempa * tempb / alpem
    IF ((b(n) == zero).AND.(ncalc > n)) ncalc = n - 1
  END DO
  GO TO 210
!-----------------------------------------------------------
! ASYMPTOTIC SERIES FOR X > 21.0
!-----------------------------------------------------------
  170   xc = SQRT(pi2/x)
  xin = (eighth/x) ** 2
  m = 11
  IF (x >= 35.0D0) m = 8
  IF (x >= 130.0D0) m = 4
  xm = four * m
!-----------------------------------------------------------
! REDUCTION OF ARGUMENT FOR SIN AND COS ROUTINES
!-----------------------------------------------------------
  t = INT(x/(twopi1+twopi2)+half)
  z = ((x-t*twopi1) - t*twopi2) - (alpha+half) / pi2
  vsin = SIN(z)
  vcos = COS(z)
  gnu = alpha + alpha
  DO  i = 1, 2
    s = ((xm-one) - gnu) * ((xm-one) + gnu) * xin * half
    t = (gnu - (xm-three)) * (gnu + (xm-three))
    capp = s * t / fact(2*m+1)
    t1 = (gnu - (xm+one)) * (gnu + (xm+one))
    capq = s * t1 / fact(2*m+2)
    xk = xm
    k = m + m
    t1 = t
    DO  j = 2, m
      xk = xk - four
      s = ((xk-one) - gnu) * ((xk-one) + gnu)
      t = (gnu - (xk-three)) * (gnu + (xk-three))
      capp = (capp + one/fact(k-1)) * s * t * xin
      capq = (capq + one/fact(k)) * s * t1 * xin
      k = k - 2
      t1 = t
    END DO
    capp = capp + one
    capq = (capq+one) * (gnu*gnu - one) * (eighth/x)
    b(i) = xc * (capp*vcos - capq*vsin)
    IF (nb == 1) GO TO 210
    t = vsin
    vsin = -vcos
    vcos = t
    gnu = gnu + two
  END DO
!-----------------------------------------------------------
! IF  NB > 2, COMPUTE J(X,ORDER+I)  I = 2, NB-1
!-----------------------------------------------------------
  IF (nb > 2) THEN
    gnu = alpha + alpha + two
    
    DO  j = 3, nb
      b(j) = gnu * b(j-1) / x - b(j-2)
      gnu = gnu + two
    END DO
  END IF
END IF
!-----------------------------------------------------------
! EXIT
!-----------------------------------------------------------
210 RETURN
! ---------- LAST CARD OF RJBESL ----------
END SUBROUTINE rjbesl



FUNCTION dgamma(x) RESULT(fn_val)
!----------------------------------------------------------------------

! THIS ROUTINE CALCULATES THE GAMMA FUNCTION FOR A REAL ARGUMENT X.
!     COMPUTATION IS BASED ON AN ALGORITHM OUTLINED IN W. J. CODY,
!     'AN OVERVIEW OF SOFTWARE DEVELOPMENT FOR SPECIAL FUNCTIONS',
!     LECTURE NOTES IN MATHEMATICS, 506, NUMERICAL ANALYSIS DUNDEE,
!     1975, G. A. WATSON (ED.), SPRINGER VERLAG, BERLIN, 1976.  THE
!     PROGRAM USES RATIONAL FUNCTIONS THAT APPROXIMATE THE GAMMA
!     FUNCTION TO AT LEAST 20 SIGNIFICANT DECIMAL DIGITS.  COEFFICIENTS
!     FOR THE APPROXIMATION OVER THE INTERVAL (1,2) ARE UNPUBLISHED.
!     THOSE FOR THE APPROXIMATION FOR X .GE. 12 ARE FROM HART, ET. AL.,
!     COMPUTER APPROXIMATIONS, WILEY AND SONS, NEW YORK, 1968.  LOWER
!     ORDER APPROXIMATIONS CAN BE SUBSTITUTED FOR THESE ON MACHINES
!     WITH LESS PRECISE ARITHMETIC.


!*******************************************************************
!*******************************************************************

! EXPLANATION OF MACHINE-DEPENDENT CONSTANTS

! EPS    - THE SMALLEST POSITIVE FLOATING-POINT NUMBER SUCH THAT
!          1.0 + EPS > 1.0
! XBIG   - THE LARGEST ARGUMENT FOR WHICH GAMMA(X) IS REPRESENTABLE
!          IN THE MACHINE, I.E., THE SOLUTION TO THE EQUATION
!                  GAMMA(XBIG) = XINF.
! XMININ - THE SMALLEST POSITIVE FLOATING-POINT NUMBER SUCH THAT
!          1/XMININ IS MACHINE REPRESENTABLE.
! XINF   - THE LARGEST MACHINE REPRESENTABLE FLOATING-POINT NUMBER.

!     APPROXIMATE VALUES FOR SOME IMPORTANT MACHINES ARE:

!         IBM/195    CDC/7600  UNIVAC/1108   VAX 11/780 (UNIX)
!          (D.P.)  (S.P.,RNDG)    (D.P.)     (S.P.)     (D.P.)

! EPS     2.221D-16  3.553E-15   1.735D-18   5.961E-08  1.388D-17
! XBIG    57.574     177.802     171.489     34.844     34.844
! XMININ  1.382D-76  3.132E-294  1.113D-308  5.883E-39  5.883D-39
! XINF    7.23D+75   1.26E+322   8.98D+307   1.70E+38   1.70D+38

!*******************************************************************
!*******************************************************************


! ERROR RETURNS

!  THE PROGRAM RETURNS THE VALUE XINF FOR SINGULARITIES OR
!     WHEN OVERFLOW WOULD OCCUR.  THE COMPUTATION IS BELIEVED
!     TO BE FREE OF UNDERFLOW AND OVERFLOW.

! OTHER SUBPROGRAMS REQUIRED (SINGLE PRECISION VERSION)

!     ALOG,EXP,FLOAT,IFIX,SIN

! OTHER SUBPROGRAMS REQUIRED (DOUBLE PRECISION VERSION)

!     DBLE,DEXP,DLOG,DSIN,FLOAT,IFIX,SNGL

!  AUTHOR: W. J. CODY
!          APPLIED MATHEMATICS DIVISION
!          ARGONNE NATIONAL LABORATORY
!          ARGONNE, IL 60439

!  LATEST MODIFICATION: MAY 18, 1982

!----------------------------------------------------------------------

IMPLICIT NONE
INTEGER, PARAMETER  :: dp = SELECTED_REAL_KIND(12, 60)

REAL (dp), INTENT(IN)  :: x
REAL (dp)              :: fn_val

REAL (dp)  :: fact, res, sum, xden, xnum, y, y1, ysq, z
INTEGER    :: i, j, n
LOGICAL    :: parity
!----------------------------------------------------------------------
!  MATHEMATICAL CONSTANTS
!----------------------------------------------------------------------
REAL (dp), PARAMETER  :: one = 1.0_dp, half = 0.5_dp, twelve = 12.0_dp,   &
                         zero = 0.0_dp
REAL (dp), PARAMETER  :: sqrtpi = 0.9189385332046727417803297_dp
REAL (dp), PARAMETER  :: pi = 3.1415926535897932384626434_dp
!----------------------------------------------------------------------
!  MACHINE DEPENDENT PARAMETERS
!----------------------------------------------------------------------
REAL (dp), PARAMETER  :: xbig = 34.844D0, xminin = 5.883D-39, eps = EPSILON(one)
REAL (dp), PARAMETER  :: xinf = 1.7014D38
!----------------------------------------------------------------------
!  NUMERATOR AND DENOMINATOR COEFFICIENTS FOR RATIONAL MINIMAX
!     APPROXIMATION OVER (1,2).
!----------------------------------------------------------------------
REAL (dp), PARAMETER  :: p(8) = (/ -1.71618513886549492533811D+0,  &
     2.47656508055759199108314D+1, -3.79804256470945635097577D+2,  &
     6.29331155312818442661052D+2,  8.66966202790413211295064D+2,  &
    -3.14512729688483675254357D+4, -3.61444134186911729807069D+4,  &
     6.64561438202405440627855D+4 /)
REAL (dp), PARAMETER  :: q(8) = (/ -3.08402300119738975254353D+1,  &
     3.15350626979604161529144D+2, -1.01515636749021914166146D+3,  &
    -3.10777167157231109440444D+3,  2.25381184209801510330112D+4,  &
     4.75584627752788110767815D+3, -1.34659959864969306392456D+5,  &
    -1.15132259675553483497211D+5 /)
!----------------------------------------------------------------------
!  COEFFICIENTS FOR MINIMAX APPROXIMATION OVER (12, INF).
!----------------------------------------------------------------------
REAL (dp), PARAMETER  :: c(7) = (/ -1.910444077728D-03, 8.4171387781295D-04,  &
    -5.952379913043012D-04, 7.93650793500350248D-04, -2.777777777777681622553D-03, &
     8.333333333333333331554247D-02, 5.7083835261D-03 /)
!----------------------------------------------------------------------
parity = .false.
fact = one
n = 0
y = x
IF (y <= zero) THEN
!----------------------------------------------------------------------
!  ARGUMENT IS NEGATIVE
!----------------------------------------------------------------------
  y = -x
  j = y
  res = y - j
  IF (res == zero) GO TO 40
  IF (j /= (j/2)*2) parity = .true.
  fact = -pi / SIN(pi*res)
  y = y + one
END IF
!----------------------------------------------------------------------
!  ARGUMENT IS POSITIVE
!----------------------------------------------------------------------
IF (y >= eps) THEN
  IF (y < twelve) THEN
    y1 = y
    IF (y < one) THEN
!----------------------------------------------------------------------
!  0.0 < ARGUMENT < 1.0
!----------------------------------------------------------------------
      z = y
      y = y + one
    ELSE
!----------------------------------------------------------------------
!  1.0 < ARGUMENT < 12.0, REDUCE ARGUMENT IF NECESSARY
!----------------------------------------------------------------------
      n = y - 1
      y = y - n
      z = y - one
    END IF
!----------------------------------------------------------------------
!  EVALUATE APPROXIMATION FOR 1.0 < ARGUMENT < 2.0
!----------------------------------------------------------------------
    xnum = zero
    xden = one
    DO  i = 1, 8
      xnum = (xnum+p(i)) * z
      xden = xden * z + q(i)
    END DO
    res = xnum / xden + one
    IF (y == y1) GO TO 50
    IF (y1 <= y) THEN
!----------------------------------------------------------------------
!  ADJUST RESULT FOR CASE  0.0 < ARGUMENT < 1.0
!----------------------------------------------------------------------
      res = res / y1
      GO TO 50
    END IF
!----------------------------------------------------------------------
!  ADJUST RESULT FOR CASE  2.0 < ARGUMENT < 12.0
!----------------------------------------------------------------------
    DO  i = 1, n
      res = res * y
      y = y + one
    END DO
    GO TO 50
  END IF
!----------------------------------------------------------------------
!  EVALUATE FOR ARGUMENT >= 12.0,
!----------------------------------------------------------------------
  IF (y > xbig) GO TO 40
  ysq = y * y
  sum = c(7)
  DO  i = 1, 6
    sum = sum / ysq + c(i)
  END DO
  sum = sum / y - y + sqrtpi
  sum = sum + (y-half) * LOG(y)
  res = EXP(sum)
  GO TO 50
END IF
!----------------------------------------------------------------------
!  ARGUMENT < EPS
!----------------------------------------------------------------------
IF (y >= xminin) THEN
  res = one / y
  GO TO 50
END IF
!----------------------------------------------------------------------
!  RETURN FOR SINGULARITIES, EXTREME ARGUMENTS, ETC.
!----------------------------------------------------------------------
40 fn_val = xinf
GO TO 60
!----------------------------------------------------------------------
!  FINAL ADJUSTMENTS AND RETURN
!----------------------------------------------------------------------
50 IF (parity) res = -res
IF (fact /= one) res = fact / res
fn_val = res
60 RETURN
! ---------- LAST CARD OF GAMMA ----------
END FUNCTION dgamma
