MODULE multidim_integrate
IMPLICIT NONE

INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15, 60)

! This version, which uses the ELF90 subset of Fortran 90, produced by
! Alan Miller:  alan @ users.bigpond.net.au
! URL: www.bigpond.net.au/amiller/

! Latest revision - 29 August 1997

CONTAINS

SUBROUTINE dcuhre(ndim, numfun, a, b, minpts, maxpts, funsub, epsabs,  &
                  epsrel, key, restar, result, abserr, neval, ifail, nsub)
!***BEGIN PROLOGUE DCUHRE
!***DATE WRITTEN   900116   (YYMMDD)
!***REVISION DATE  900116   (YYMMDD)
!***CATEGORY NO. H2B1A1
!***AUTHOR
!        Jarle Berntsen, The Computing Centre,
!        University of Bergen, Thormohlens gt. 55,
!        N-5008 Bergen, Norway
!        Phone..  47-5-544055
!        Email..  jarle@eik.ii.uib.no
!        Terje O. Espelid, Department of Informatics,
!        University of Bergen, Thormohlens gt. 55,
!        N-5008 Bergen, Norway
!        Phone..  47-5-544180
!        Email..  terje@eik.ii.uib.no
!        Alan Genz, Computer Science Department, Washington State
!        University, Pullman, WA 99163-2752, USA
!        Email..  acg@eecs.wsu.edu
!***KEYWORDS automatic multidimensional integrator,
!        n-dimensional hyper-rectangles,
!        general purpose, global adaptive
!***PURPOSE  The routine calculates an approximation to a given
!            vector of definite integrals

!      B(1) B(2)     B(NDIM)
!     I    I    ... I       (F ,F ,...,F      ) DX(NDIM)...DX(2)DX(1),
!      A(1) A(2)     A(NDIM)  1  2      NUMFUN

!       where F = F (X ,X ,...,X    ), I = 1,2,...,NUMFUN.
!              I   I  1  2      NDIM

!        hopefully satisfying for each component of I the following
!        claim for accuracy:
!        ABS(I(K)-RESULT(K)).LE.MAX(EPSABS,EPSREL*ABS(I(K)))
!***DESCRIPTION Computation of integrals over hyper-rectangular regions.
!        DCUHRE is a driver for the integration routine
!        DADHRE, which repeatedly subdivides the region
!        of integration and estimates the integrals and the
!        errors over the subregions with greatest
!        estimated errors until the error request
!        is met or MAXPTS function evaluations have been used.

!        For NDIM = 2 the default integration rule is of
!        degree 13 and uses 65 evaluation points.
!        For NDIM = 3 the default integration rule is of
!        degree 11 and uses 127 evaluation points.
!        For NDIM greater then 3 the default integration rule
!        is of degree 9 and uses NUM evaluation points where
!          NUM = 1 + 4*2*NDIM + 2*NDIM*(NDIM-1) + 4*NDIM*(NDIM-1) +
!                4*NDIM*(NDIM-1)*(NDIM-2)/3 + 2**NDIM
!        The degree 9 rule may also be applied for NDIM = 2
!        and NDIM = 3.
!        A rule of degree 7 is available in all dimensions.
!        The number of evaluation
!        points used by the degree 7 rule is
!          NUM = 1 + 3*2*NDIM + 2*NDIM*(NDIM-1) + 2**NDIM

!        When DCUHRE computes estimates to a vector of
!        integrals, all components of the vector are given
!        the same treatment. That is, I(F ) and I(F ) for
!                                        J         K
!        J not equal to K, are estimated with the same
!        subdivision of the region of integration.
!        For integrals with enough similarity, we may save
!        time by applying DCUHRE to all integrands in one call.
!        For integrals that varies continuously as functions of
!        some parameter, the estimates produced by DCUHRE will
!        also vary continuously when the same subdivision is
!        applied to all components. This will generally not be
!        the case when the different components are given
!        separate treatment.

!        On the other hand this feature should be used with
!        caution when the different components of the integrals
!        require clearly different subdivisions.

!   ON ENTRY

!   NDIM   Integer.
!          Number of variables. 1 < NDIM <=  15.
!   NUMFUN Integer.
!          Number of components of the integral.
!   A      Real array of dimension NDIM.
!          Lower limits of integration.
!   B      Real array of dimension NDIM.
!          Upper limits of integration.
!   MINPTS Integer.
!          Minimum number of function evaluations.
!   MAXPTS Integer.
!          Maximum number of function evaluations.
!          The number of function evaluations over each subregion is NUM.
!          If (KEY = 0 or KEY = 1) and (NDIM = 2) Then
!            NUM = 65
!          Elseif (KEY = 0 or KEY = 2) and (NDIM = 3) Then
!            NUM = 127
!          Elseif (KEY = 0 and NDIM > 3) or (KEY = 3) Then
!            NUM = 1 + 4*2*NDIM + 2*NDIM*(NDIM-1) + 4*NDIM*(NDIM-1) +
!                  4*NDIM*(NDIM-1)*(NDIM-2)/3 + 2**NDIM
!          Elseif (KEY = 4) Then
!            NUM = 1 + 3*2*NDIM + 2*NDIM*(NDIM-1) + 2**NDIM
!          MAXPTS >= 3*NUM and MAXPTS >= MINPTS
!          For 3 < NDIM < 13 the minimum values for MAXPTS are:
!           NDIM =    4   5   6    7    8    9    10   11    12
!          KEY = 3:  459 819 1359 2151 3315 5067 7815 12351 20235
!          KEY = 4:  195 309  483  765 1251 2133 3795  7005 13299
!   FUNSUB Externally declared subroutine for computing all components of the
!          integrand at the given evaluation point.
!          It must have parameters (NDIM,X,NUMFUN,FUNVLS)
!          Input parameters:
!            NDIM   Integer that defines the dimension of the
!                   integral.
!            X      Real array of dimension NDIM
!                   that defines the evaluation point.
!            NUMFUN Integer that defines the number of
!                   components of I.
!          Output parameter:
!            FUNVLS Real array of dimension NUMFUN
!                   that defines NUMFUN components of the integrand.

!   EPSABS Real.
!          Requested absolute error.
!   EPSREL Real.
!          Requested relative error.
!   KEY    Integer.
!          Key to selected local integration rule.
!          KEY = 0 is the default value.
!                For NDIM = 2 the degree 13 rule is selected.
!                For NDIM = 3 the degree 11 rule is selected.
!                For NDIM > 3 the degree  9 rule is selected.
!          KEY = 1 gives the user the 2 dimensional degree 13
!                integration rule that uses 65 evaluation points.
!          KEY = 2 gives the user the 3 dimensional degree 11
!                integration rule that uses 127 evaluation points.
!          KEY = 3 gives the user the degree 9 integration rule.
!          KEY = 4 gives the user the degree 7 integration rule.
!                This is the recommended rule for problems that
!                require great adaptivity.
!   RESTAR Integer.
!          If RESTAR = 0, this is the first attempt to compute
!          the integral.
!          If RESTAR = 1, then we restart a previous attempt.
!          In this case the only parameters for DCUHRE that may
!          be changed (with respect to the previous call of DCUHRE)
!          are MINPTS, MAXPTS, EPSABS, EPSREL and RESTAR.
!   NSUB   Integer.
!          If RESTAR = 1, then NSUB contains the number of subregions from
!          the previous call,

! ON RETURN

!   RESULT Real array of dimension NUMFUN.
!          Approximations to all components of the integral.
!   ABSERR Real array of dimension NUMFUN.
!          Estimates of absolute errors.
!   NEVAL  Integer.
!          Number of function evaluations used by DCUHRE.
!   IFAIL  Integer.
!          IFAIL = 0 for normal exit, when ABSERR(K) <=  EPSABS or
!            ABSERR(K) <=  ABS(RESULT(K))*EPSREL with MAXPTS or less
!            function evaluations for all values of K,
!            1 <= K <= NUMFUN .
!          IFAIL = 1 if MAXPTS was too small for DCUHRE
!            to obtain the required accuracy. In this case DCUHRE
!            returns values of RESULT with estimated absolute
!            errors ABSERR.
!          IFAIL = 2 if KEY is less than 0 or KEY greater than 4.
!          IFAIL = 3 if NDIM is less than 2 or NDIM greater than 15.
!          IFAIL = 4 if KEY = 1 and NDIM not equal to 2.
!          IFAIL = 5 if KEY = 2 and NDIM not equal to 3.
!          IFAIL = 6 if NUMFUN is less than 1.
!          IFAIL = 7 if volume of region of integration is zero.
!          IFAIL = 8 if MAXPTS is less than 3*NUM.
!          IFAIL = 9 if MAXPTS is less than MINPTS.
!          IFAIL = 10 if EPSABS < 0 and EPSREL < 0.
!          IFAIL = 11 if NW is too small.
!          IFAIL = 12 if unlegal RESTAR.
!   NSUB   Integer.
!          Contains the number of subregions, in case needed for a restart.

!***LONG DESCRIPTION

!   The information for each subregion is contained in:
!   VALUES, ERRORS, CENTRS, HWIDTS, GREATE, DIR, OLDRES and WORK.
!   VALUES contains the estimated values of the integrals.
!   ERRORS contains the estimated errors.
!   CENTRS contains the centers of the subregions.
!   HWIDTS contains the half widths of the subregions.
!   GREATE contains the greatest estimated error for each subregion.
!   DIR    contains the directions for further subdivision.
!   OLDRES is used as a work array in DADHRE.

!   The data structures for the subregions are in DADHRE organized
!   as a heap, and the size of GREATE(I) defines the position of
!   region I in the heap. The heap is maintained by the program DTRHRE.

!   The subroutine DADHRE is written for efficient execution on shared
!   memory parallel computer. On a computer with NPROC processors we will
!   in each subdivision step divide MDIV regions, where MDIV is
!   chosen such that MOD(2*MDIV,NPROC) = 0, in totally 2*MDIV new regions.
!   Each processor will then compute estimates of the integrals and errors
!   over 2*MDIV/NPROC subregions in each subdivision step.
!   The subroutine for estimating the integral and the error over
!   each subregion, DRLHRE, uses WORK2 as a work array.
!   We must make sure that each processor writes its results to
!   separate parts of the memory, and therefore the sizes of WORK and
!   WORK2 are functions of MDIV.
!   In order to achieve parallel processing of subregions, compiler
!   directives should be placed in front of the DO 200
!   loop in DADHRE on machines like Alliant and CRAY.

!***REFERENCES
!   J.Berntsen, T.O.Espelid and A.Genz, An Adaptive Algorithm
!   for the Approximate Calculation of Multiple Integrals,
!   To be published.

!   J.Berntsen, T.O.Espelid and A.Genz, DCUHRE: An Adaptive
!   Multidimensional Integration Routine for a Vector of
!   Integrals, To be published.

!***ROUTINES CALLED DCHHRE,DADHRE
!***END PROLOGUE DCUHRE

!   Global variables.

INTEGER, INTENT(IN)     :: ndim, numfun, minpts, maxpts, key, restar
INTEGER, INTENT(IN OUT) :: nsub
INTEGER, INTENT(OUT)    :: neval, ifail
REAL (dp), INTENT(IN)   :: a(:), b(:), epsabs, epsrel
REAL (dp), INTENT(OUT)  :: result(:), abserr(:)

! Dimensions of arguments:
! a(ndim), b(ndim), result(numfun), abserr(numfun), work(nw)

INTERFACE
  SUBROUTINE funsub(ndim, z, nfun, f)
    IMPLICIT NONE
    INTEGER, PARAMETER     :: dp = SELECTED_REAL_KIND(15, 60)
    INTEGER, INTENT(IN)    :: ndim, nfun
    REAL (dp), INTENT(IN)  :: z(:)
    REAL (dp), INTENT(OUT) :: f(:)
  END SUBROUTINE funsub
END INTERFACE

!   Local variables.

!   MDIV   Integer.
!          MDIV is the number of subregions that are divided in
!          each subdivision step in DADHRE.
!          MDIV is chosen default to 1.
!          For efficient execution on parallel computers
!          with NPROC processors MDIV should be set equal to
!          the smallest integer such that MOD(2*MDIV,NPROC) = 0.
!   MAXDIM Integer.
!          The maximum allowed value of NDIM.
!   MAXWT  Integer. The maximum number of weights used by the
!          integration rule.
!   WTLENG Integer.
!          The number of generators used by the selected rule.
!   MAXSUB Integer.
!          The maximum allowed number of subdivisions
!          for the given values of KEY, NDIM and MAXPTS.
!   MINSUB Integer.
!          The minimum allowed number of subregions for the given
!          values of MINPTS, KEY and NDIM.
!   WRKSUB Integer.
!          The maximum allowed number of subregions as a function
!          of NW, NUMFUN, NDIM and MDIV. This determines the length
!          of the main work arrays.
!   NUM    Integer. The number of integrand evaluations needed
!          over each subregion.

INTEGER            :: wtleng, maxsub, minsub
INTEGER            :: num, keyf
INTEGER, PARAMETER :: mdiv=1, maxdim=15

!***FIRST EXECUTABLE STATEMENT DCUHRE

!   Compute NUM, WTLENG, MAXSUB and MINSUB, and check the input parameters.

CALL dchhre(maxdim, ndim, numfun, a, b, minpts, maxpts, epsabs,  &
            epsrel, key, restar, num, maxsub, minsub, keyf, ifail, wtleng)

IF (ifail /= 0) THEN
  GO TO 999
END IF

!   Compute the size of the temporary work space needed in DADHRE.

CALL dadhre(ndim, numfun, mdiv, a, b, minsub, maxsub, funsub, epsabs,  &
            epsrel, keyf, restar, num, wtleng, result, abserr,  &
            neval, nsub, ifail)
999 RETURN

!***END DCUHRE

END SUBROUTINE dcuhre

SUBROUTINE dchhre(maxdim, ndim, numfun, a, b, minpts, maxpts, epsabs,  &
                  epsrel, key, restar, num, maxsub, minsub, keyf, ifail, &
                  wtleng)
!***BEGIN PROLOGUE DCHHRE
!***PURPOSE  DCHHRE checks the validity of the
!            input parameters to DCUHRE.
!***DESCRIPTION
!            DCHHRE computes NUM, MAXSUB, MINSUB, KEYF, WTLENG and
!            IFAIL as functions of the input parameters to DCUHRE,
!            and checks the validity of the input parameters to DCUHRE.

!   ON ENTRY

!     MAXDIM Integer.
!            The maximum allowed number of dimensions.
!     NDIM   Integer.
!            Number of variables. 1 < NDIM <= MAXDIM.
!     NUMFUN Integer.
!            Number of components of the integral.
!     A      Real array of dimension NDIM.
!            Lower limits of integration.
!     B      Real array of dimension NDIM.
!            Upper limits of integration.
!     MINPTS Integer.
!            Minimum number of function evaluations.
!     MAXPTS Integer.
!            Maximum number of function evaluations.
!            The number of function evaluations over each subregion
!            is NUM.
!            If (KEY = 0 or KEY = 1) and (NDIM = 2) Then
!              NUM = 65
!            Elseif (KEY = 0 or KEY = 2) and (NDIM = 3) Then
!              NUM = 127
!            Elseif (KEY = 0 and NDIM > 3) or (KEY = 3) Then
!              NUM = 1 + 4*2*NDIM + 2*NDIM*(NDIM-1) + 4*NDIM*(NDIM-1) +
!                    4*NDIM*(NDIM-1)*(NDIM-2)/3 + 2**NDIM
!            Elseif (KEY = 4) Then
!              NUM = 1 + 3*2*NDIM + 2*NDIM*(NDIM-1) + 2**NDIM
!            MAXPTS >= 3*NUM and MAXPTS >= MINPTS
!     EPSABS Real.
!            Requested absolute error.
!     EPSREL Real.
!            Requested relative error.
!     KEY    Integer.
!            Key to selected local integration rule.
!            KEY = 0 is the default value.
!                  For NDIM = 2 the degree 13 rule is selected.
!                  For NDIM = 3 the degree 11 rule is selected.
!                  For NDIM > 3 the degree  9 rule is selected.
!            KEY = 1 gives the user the 2 dimensional degree 13
!                  integration rule that uses 65 evaluation points.
!            KEY = 2 gives the user the 3 dimensional degree 11
!                  integration rule that uses 127 evaluation points.
!            KEY = 3 gives the user the degree 9 integration rule.
!            KEY = 4 gives the user the degree 7 integration rule.
!                  This is the recommended rule for problems that
!                  require great adaptivity.
!     RESTAR Integer.
!            If RESTAR = 0, this is the first attempt to compute
!            the integral.
!            If RESTAR = 1, then we restart a previous attempt.

!   ON RETURN

!     NUM    Integer.
!            The number of function evaluations over each subregion.
!     MAXSUB Integer.
!            The maximum allowed number of subregions for the
!            given values of MAXPTS, KEY and NDIM.
!     MINSUB Integer.
!            The minimum allowed number of subregions for the given
!            values of MINPTS, KEY and NDIM.
!     IFAIL  Integer.
!            IFAIL = 0 for normal exit.
!            IFAIL = 2 if KEY is less than 0 or KEY greater than 4.
!            IFAIL = 3 if NDIM is less than 2 or NDIM greater than MAXDIM.
!            IFAIL = 4 if KEY = 1 and NDIM not equal to 2.
!            IFAIL = 5 if KEY = 2 and NDIM not equal to 3.
!            IFAIL = 6 if NUMFUN less than 1.
!            IFAIL = 7 if volume of region of integration is zero.
!            IFAIL = 8 if MAXPTS is less than 3*NUM.
!            IFAIL = 9 if MAXPTS is less than MINPTS.
!            IFAIL = 10 if EPSABS < 0 and EPSREL < 0.
!            IFAIL = 11 if NW is too small.
!            IFAIL = 12 if unlegal RESTAR.
!     KEYF   Integer.
!            Key to selected integration rule.
!     WTLENG Integer.
!            The number of generators of the chosen integration rule.

!***ROUTINES CALLED-NONE
!***END PROLOGUE DCHHRE

!   Global variables.

INTEGER, INTENT(IN)   :: maxdim, ndim, numfun, minpts, maxpts, key, restar
INTEGER, INTENT(OUT)  :: num, minsub, maxsub, keyf, ifail, wtleng
REAL (dp), INTENT(IN) :: a(:), b(:), epsabs, epsrel

!   Local variables.

INTEGER :: j

!***FIRST EXECUTABLE STATEMENT DCHHRE

ifail = 0

!   Check on legal KEY.

IF (key < 0 .OR. key > 4) THEN
  ifail = 2
  GO TO 999
END IF

!   Check on legal NDIM.

IF (ndim < 2 .OR. ndim > maxdim) THEN
  ifail = 3
  GO TO 999
END IF

!   For KEY = 1, NDIM must be equal to 2.

IF (key == 1 .AND. ndim /= 2) THEN
  ifail = 4
  GO TO 999
END IF

!   For KEY = 2, NDIM must be equal to 3.

IF (key == 2 .AND. ndim /= 3) THEN
  ifail = 5
  GO TO 999
END IF

!   For KEY = 0, we point at the selected integration rule.

IF (key == 0) THEN
  IF (ndim == 2) THEN
    keyf = 1
  ELSE IF (ndim == 3) THEN
    keyf = 2
  ELSE
    keyf = 3
  END IF
ELSE
  keyf = key
END IF

!   Compute NUM and WTLENG as a function of KEYF and NDIM.

IF (keyf == 1) THEN
  num = 65
  wtleng = 14
ELSE IF (keyf == 2) THEN
  num = 127
  wtleng = 13
ELSE IF (keyf == 3) THEN
  num = 1 + 4*2*ndim + 2*ndim* (ndim-1) + 4*ndim* (ndim-1) +  &
            4*ndim* (ndim-1)* (ndim-2)/3 + 2**ndim
  wtleng = 9
  IF (ndim == 2) wtleng = 8
ELSE IF (keyf == 4) THEN
  num = 1 + 3*2*ndim + 2*ndim* (ndim-1) + 2**ndim
  wtleng = 6
END IF

!   Compute MAXSUB.

maxsub = (maxpts-num)/ (2*num) + 1

!   Compute MINSUB.

minsub = (minpts-num)/ (2*num) + 1
IF (MOD(minpts-num, 2*num) /= 0) THEN
  minsub = minsub + 1
END IF
minsub = MAX(2,minsub)

!   Check on positive NUMFUN.

IF (numfun < 1) THEN
  ifail = 6
  GO TO 999
END IF

!   Check on legal upper and lower limits of integration.

DO j = 1, ndim
  IF (a(j)-b(j) == 0) THEN
    ifail = 7
    GO TO 999
  END IF
END DO

!   Check on MAXPTS < 3*NUM.

IF (maxpts < 3*num) THEN
  ifail = 8
  GO TO 999
END IF

!   Check on MAXPTS >= MINPTS.

IF (maxpts < minpts) THEN
  ifail = 9
  GO TO 999
END IF

!   Check on legal accuracy requests.

IF (epsabs < 0 .AND. epsrel < 0) THEN
  ifail = 10
  GO TO 999
END IF

!    Check on legal RESTAR.

IF (restar /= 0 .AND. restar /= 1) THEN
  ifail = 12
  GO TO 999
END IF
999 RETURN

!***END DCHHRE

END SUBROUTINE dchhre

SUBROUTINE dadhre(ndim, numfun, mdiv, a, b, minsub, maxsub, funsub, epsabs,   &
                  epsrel, key, restar, num, wtleng, result, abserr,     &
                  neval, nsub, ifail)
!***BEGIN PROLOGUE DADHRE
!***KEYWORDS automatic multidimensional integrator,
!            n-dimensional hyper-rectangles,
!            general purpose, global adaptive
!***PURPOSE  The routine calculates an approximation to a given
!            vector of definite integrals, I, over a hyper-rectangular
!            region hopefully satisfying for each component of I the
!            following claim for accuracy:
!            ABS(I(K)-RESULT(K)).LE.MAX(EPSABS,EPSREL*ABS(I(K)))
!***DESCRIPTION Computation of integrals over hyper-rectangular
!            regions.
!            DADHRE repeatedly subdivides the region
!            of integration and estimates the integrals and the
!            errors over the subregions with  greatest
!            estimated errors until the error request
!            is met or MAXSUB subregions are stored.
!            The regions are divided in two equally sized parts along
!            the direction with greatest absolute fourth divided
!            difference.

!   ON ENTRY

!     NDIM   Integer.
!            Number of variables. 1 < NDIM <= MAXDIM.
!     NUMFUN Integer.
!            Number of components of the integral.
!     MDIV   Integer.
!            Defines the number of new subregions that are divided
!            in each subdivision step.
!     A      Real array of dimension NDIM.
!            Lower limits of integration.
!     B      Real array of dimension NDIM.
!            Upper limits of integration.
!     MINSUB Integer.
!            The computations proceed until there are at least
!            MINSUB subregions in the data structure.
!     MAXSUB Integer.
!            The computations proceed until there are at most
!            MAXSUB subregions in the data structure.

!     FUNSUB Externally declared subroutine for computing
!            all components of the integrand in the given
!            evaluation point.
!            It must have parameters (NDIM,X,NUMFUN,FUNVLS)
!            Input parameters:
!              NDIM   Integer that defines the dimension of the
!                     integral.
!              X      Real array of dimension NDIM
!                     that defines the evaluation point.
!              NUMFUN Integer that defines the number of
!                     components of I.
!            Output parameter:
!              FUNVLS Real array of dimension NUMFUN
!                     that defines NUMFUN components of the integrand.

!     EPSABS Real.
!            Requested absolute error.
!     EPSREL Real.
!            Requested relative error.
!     KEY    Integer.
!            Key to selected local integration rule.
!            KEY = 0 is the default value.
!                  For NDIM = 2 the degree 13 rule is selected.
!                  For NDIM = 3 the degree 11 rule is selected.
!                  For NDIM > 3 the degree  9 rule is selected.
!            KEY = 1 gives the user the 2 dimensional degree 13
!                  integration rule that uses 65 evaluation points.
!            KEY = 2 gives the user the 3 dimensional degree 11
!                  integration rule that uses 127 evaluation points.
!            KEY = 3 gives the user the degree 9 integration rule.
!            KEY = 4 gives the user the degree 7 integration rule.
!                  This is the recommended rule for problems that
!                  require great adaptivity.
!     RESTAR Integer.
!            If RESTAR = 0, this is the first attempt to compute
!            the integral.
!            If RESTAR = 1, then we restart a previous attempt.
!            (In this case the output parameters from DADHRE
!            must not be changed since the last
!            exit from DADHRE.)
!     NUM    Integer.
!            The number of function evaluations over each subregion.
!     WTLENG Integer.
!            The number of weights in the basic integration rule.
!     NSUB   Integer.
!            If RESTAR = 1, then NSUB must specify the number
!            of subregions stored in the previous call to DADHRE.

!   ON RETURN

!     RESULT Real array of dimension NUMFUN.
!            Approximations to all components of the integral.
!     ABSERR Real array of dimension NUMFUN.
!            Estimates of absolute accuracies.
!     NEVAL  Integer.
!            Number of function evaluations used by DADHRE.
!     NSUB   Integer.
!            Number of stored subregions.
!     IFAIL  Integer.
!            IFAIL = 0 for normal exit, when ABSERR(K) <=  EPSABS or
!              ABSERR(K) <=  ABS(RESULT(K))*EPSREL with MAXSUB or less
!              subregions processed for all values of K,
!              1 <=  K <=  NUMFUN.
!            IFAIL = 1 if MAXSUB was too small for DADHRE
!              to obtain the required accuracy. In this case DADHRE
!              returns values of RESULT with estimated absolute
!              accuracies ABSERR.
!
! The following occupied workspace provided by DCUHRE in the Fortran 77 code:
!
!     VALUES Real array of dimension (NUMFUN,MAXSUB).
!            Used to store estimated values of the integrals
!            over the subregions.
!     ERRORS Real array of dimension (NUMFUN,MAXSUB).
!            Used to store the corresponding estimated errors.
!     CENTRS Real array of dimension (NDIM,MAXSUB).
!            Used to store the centers of the stored subregions.
!     HWIDTS Real array of dimension (NDIM,MAXSUB).
!            Used to store the half widths of the stored subregions.
!     GREATE Real array of dimension MAXSUB.
!            Used to store the greatest estimated errors in
!            all subregions.
!     DIR    Real array of dimension MAXSUB.
!            DIR is used to store the directions for
!            further subdivision.
!     OLDRES Real array of dimension (NUMFUN,MDIV).
!            Used to store old estimates of the integrals over the
!            subregions.
!     WORK   Real array of dimension LENW.
!            Used  in DRLHRE and DTRHRE.
!     G      Real array of dimension (NDIM,WTLENG,2*MDIV).
!            The fully symmetric sum generators for the rules.
!            G(1,J,1),...,G(NDIM,J,1) are the generators for the
!            points associated with the Jth weights.
!            When MDIV subregions are divided in 2*MDIV
!            subregions, the subregions may be processed on different
!            processors and we must make a copy of the generators
!            for each processor.
!     W      Real array of dimension (5,WTLENG).
!            The weights for the basic and null rules.
!            W(1,1), ..., W(1,WTLENG) are weights for the basic rule.
!            W(I,1), ..., W(I,WTLENG) , for I > 1 are null rule weights.
!     RULPTS Real array of dimension WTLENG.
!            Work array used in DINHRE.
!     CENTER Real array of dimension NDIM.
!            Work array used in DTRHRE.
!     HWIDTH Real array of dimension NDIM.
!            Work array used in DTRHRE.
!     X      Real array of dimension (NDIM,2*MDIV).
!            Work array used in DRLHRE.
!     SCALES Real array of dimension (3,WTLENG).
!            Work array used by DINHRE and DRLHRE.
!     NORMS  Real array of dimension (3,WTLENG).
!            Work array used by DINHRE and DRLHRE.

!***REFERENCES

!   P. van Dooren and L. de Ridder, Algorithm 6, An adaptive algorithm
!   for numerical integration over an n-dimensional cube, J.Comput.Appl.
!   Math. 2(1976)207-217.

!   A.C.Genz and A.A.Malik, Algorithm 019. Remarks on algorithm 006:
!   An adaptive algorithm for numerical integration over an
!   N-dimensional rectangular region, J.Comput.Appl.Math. 6(1980)295-302.

!***ROUTINES CALLED DTRHRE, DINHRE, DRLHRE
!***END PROLOGUE DADHRE

!   Global variables.

INTEGER, INTENT(IN)     :: num, ndim, numfun, mdiv, minsub, maxsub, key,  &
                           restar, wtleng
INTEGER, INTENT(IN OUT) :: nsub
INTEGER, INTENT(OUT)    :: neval, ifail
REAL (dp), INTENT(IN)   :: a(:), b(:), epsabs, epsrel
REAL (dp), INTENT(OUT)  :: result(:), abserr(:)

INTERFACE
  SUBROUTINE funsub(ndim, z, nfun, f)
    IMPLICIT NONE
    INTEGER, PARAMETER     :: dp = SELECTED_REAL_KIND(15, 60)
    INTEGER, INTENT(IN)    :: ndim, nfun
    REAL (dp), INTENT(IN)  :: z(:)
    REAL (dp), INTENT(OUT) :: f(:)
  END SUBROUTINE funsub
END INTERFACE

!   Local variables.

!   INTSGN is used to get correct sign on the integral.
!   SBRGNS is the number of stored subregions.
!   NDIV   The number of subregions to be divided in each main step.
!   POINTR Pointer to the position in the data structure where
!          the new subregions are to be stored.
!   DIRECT Direction of subdivision.
!   ERRCOF Heuristic error coeff. defined in DINHRE and used by DRLHRE
!          and DADHRE.

INTEGER   :: i, j, k
INTEGER   :: intsgn, sbrgns
INTEGER   :: ndiv, pointr, DIRECT, INDEX
REAL (dp) :: oldcen, est1, est2, errcof(6)

! The following automatic arrays were previously part of the workspace
! passed from DCUHRE.

REAL (dp) :: values(numfun,maxsub), errors(numfun,maxsub), &
             centrs(ndim,maxsub), hwidts(ndim,maxsub), greate(maxsub),  &
             dir(maxsub), oldres(numfun,mdiv), g(ndim,wtleng,2*mdiv),   &
             w(5,wtleng), center(ndim), hwidth(ndim), scales(3,wtleng), &
             norms(3,wtleng)

!***FIRST EXECUTABLE STATEMENT DADHRE

!   Get the correct sign on the integral.

intsgn = 1
DO j = 1, ndim
  IF (b(j) < a(j)) THEN
    intsgn = - intsgn
  END IF
END DO

!   Call DINHRE to compute the weights and abscissas of
!   the function evaluation points.

CALL dinhre(ndim, key, wtleng, w, g(:,:,1), errcof, scales, norms)

!   If RESTAR = 1, then this is a restart run.

IF (restar == 1) THEN
  sbrgns = nsub
  GO TO 110
END IF

!   Initialize the SBRGNS, CENTRS and HWIDTS.

sbrgns = 1
DO j = 1, ndim
  centrs(j,1) = (a(j)+b(j))/2
  hwidts(j,1) = ABS(b(j)-a(j))/2
END DO

!   Initialize RESULT, ABSERR and NEVAL.

DO j = 1, numfun
  result(j) = 0
  abserr(j) = 0
END DO
neval = 0

!   Apply DRLHRE over the whole region.

CALL drlhre(ndim, centrs(:,1), hwidts(:,1), wtleng, g(:,:,1), w, errcof,  &
            numfun, funsub, scales, norms, values(:,1), errors(:,1), dir(1))
neval = neval + num

!   Add the computed values to RESULT and ABSERR.

DO j = 1, numfun
  result(j) = result(j) + values(j,1)
END DO
DO j = 1, numfun
  abserr(j) = abserr(j) + errors(j,1)
END DO

!   Store results in heap.

INDEX = 1
CALL dtrhre(2, ndim, numfun, INDEX, values, errors, centrs, hwidts,  &
            greate, center, hwidth, dir)

!***End initialisation.

!***Begin loop while the error is too great,
!   and SBRGNS+1 is less than MAXSUB.

110 IF (sbrgns+1 <= maxsub) THEN

!   If we are allowed to divide further,
!   prepare to apply basic rule over each half of the
!   NDIV subregions with greatest errors.
!   If MAXSUB is great enough, NDIV = MDIV

  IF (mdiv > 1) THEN
    ndiv = maxsub - sbrgns
    ndiv = MIN(ndiv, mdiv, sbrgns)
  ELSE
    ndiv = 1
  END IF

!   Divide the NDIV subregions in two halves, and compute
!   integral and error over each half.

  DO i = 1, ndiv
    pointr = sbrgns + ndiv + 1 - i

!   Adjust RESULT and ABSERR.

    DO j = 1, numfun
      result(j) = result(j) - values(j,1)
      abserr(j) = abserr(j) - errors(j,1)
    END DO

!   Compute first half region.

    DO j = 1, ndim
      centrs(j,pointr) = centrs(j,1)
      hwidts(j,pointr) = hwidts(j,1)
    END DO
    DIRECT = dir(1)
    dir(pointr) = DIRECT
    hwidts(DIRECT,pointr) = hwidts(DIRECT,1)/2
    oldcen = centrs(DIRECT,1)
    centrs(DIRECT,pointr) = oldcen - hwidts(DIRECT,pointr)

!   Save the computed values of the integrals.

    DO j = 1, numfun
      oldres(j,ndiv-i+1) = values(j,1)
    END DO

!   Adjust the heap.

    CALL dtrhre(1, ndim, numfun, sbrgns, values, errors, centrs,  &
                hwidts, greate, center, hwidth, dir)

!   Compute second half region.

    DO j = 1, ndim
      centrs(j,pointr-1) = centrs(j,pointr)
      hwidts(j,pointr-1) = hwidts(j,pointr)
    END DO
    centrs(DIRECT,pointr-1) = oldcen + hwidts(DIRECT,pointr)
    hwidts(DIRECT,pointr-1) = hwidts(DIRECT,pointr)
    dir(pointr-1) = DIRECT
  END DO

!   Make copies of the generators for each processor.

  DO i = 2, 2*ndiv
    DO j = 1, ndim
      DO k = 1, wtleng
        g(j,k,i) = g(j,k,1)
      END DO
    END DO
  END DO

!   Apply basic rule.

  DO i = 1, 2*ndiv
    INDEX = sbrgns + i
    CALL drlhre(ndim, centrs(:,INDEX), hwidts(:,INDEX), wtleng, g(:,:,i), w, &
                errcof, numfun, funsub, scales, norms, values(:,INDEX),   &
                errors(:,INDEX), dir(INDEX))
  END DO
  neval = neval + 2*ndiv*num

!   Add new contributions to RESULT.

  DO i = 1, 2*ndiv
    DO j = 1, numfun
      result(j) = result(j) + values(j,sbrgns+i)
    END DO
  END DO

!   Check consistency of results and if necessary adjust
!   the estimated errors.

  DO i = 1, ndiv
    greate(sbrgns+2*i-1) = 0
    greate(sbrgns+2*i) = 0
    DO j = 1, numfun
      est1 = ABS(oldres(j,i)- (values(j,sbrgns+2*i-1)+values(j,sbrgns+2*i)))
      est2 = errors(j, sbrgns+2*i-1) + errors(j, sbrgns+2*i)
      IF (est2 > 0) THEN
        errors(j,sbrgns+2*i-1) = errors(j,sbrgns+2*i-1)*(1+errcof(5)*est1/est2)
        errors(j,sbrgns+2*i) = errors(j,sbrgns+2*i)*(1+errcof(5)*est1/est2)
      END IF
      errors(j,sbrgns+2*i-1) = errors(j,sbrgns+2*i-1) +errcof(6)*est1
      errors(j,sbrgns+2*i) = errors(j,sbrgns+2*i) +errcof(6)*est1
      IF (errors(j,sbrgns+2*i-1) > greate(sbrgns+2*i-1)) THEN
        greate(sbrgns+2*i-1) = errors(j,sbrgns+2*i-1)
      END IF
      IF (errors(j,sbrgns+2*i) > greate(sbrgns+2*i)) THEN
        greate(sbrgns+2*i) = errors(j,sbrgns+2*i)
      END IF
      abserr(j) = abserr(j) + errors(j,sbrgns+2*i-1) + errors(j,sbrgns+2*i)
    END DO
  END DO

!   Store results in heap.

  DO i = 1, 2*ndiv
    INDEX = sbrgns + i
    CALL dtrhre(2, ndim, numfun, INDEX, values, errors, centrs,  &
                hwidts, greate, center, hwidth, dir)
  END DO
  sbrgns = sbrgns + 2*ndiv

!   Check for termination.

  IF (sbrgns < minsub) THEN
    GO TO 110
  END IF
  DO j = 1, numfun
    IF (abserr(j) > epsrel*ABS(result(j)) .AND. abserr(j) > epsabs) THEN
      GO TO 110
    END IF
  END DO
  ifail = 0
  GO TO 499

!   Else we did not succeed with the
!   given value of MAXSUB.

ELSE
  ifail = 1
END IF

!   Compute more accurate values of RESULT and ABSERR.

499 DO j = 1, numfun
  result(j) = 0
  abserr(j) = 0
END DO
DO i = 1, sbrgns
  DO j = 1, numfun
    result(j) = result(j) + values(j,i)
    abserr(j) = abserr(j) + errors(j,i)
  END DO
END DO

!   Compute correct sign on the integral.

DO j = 1, numfun
  result(j) = result(j)*intsgn
END DO
nsub = sbrgns
RETURN

!***END DADHRE

END SUBROUTINE dadhre

SUBROUTINE dinhre(ndim, key, wtleng, w, g, errcof, scales, norms)
!***BEGIN PROLOGUE DINHRE
!***PURPOSE DINHRE computes abscissas and weights of the integration
!            rule and the null rules to be used in error estimation.
!            These are computed as functions of NDIM and KEY.
!***DESCRIPTION DINHRE will for given values of NDIM and KEY compute or
!            select the correct values of the abscissas and
!            corresponding weights for different
!            integration rules and null rules and assign them to G and W.
!            The heuristic error coefficients ERRCOF
!            will be computed as a function of KEY.
!            Scaling factors SCALES and normalization factors NORMS
!            used in the error estimation are computed.

!   ON ENTRY

!     NDIM   Integer.
!            Number of variables.
!     KEY    Integer.
!            Key to selected local integration rule.
!     WTLENG Integer.
!            The number of weights in each of the rules.

!   ON RETURN

!     W      Real array of dimension (5,WTLENG).
!            The weights for the basic and null rules.
!            W(1,1), ...,W(1,WTLENG) are weights for the basic rule.
!            W(I,1), ...,W(I,WTLENG), for I > 1 are null rule weights.
!     G      Real array of dimension (NDIM,WTLENG).
!            The fully symmetric sum generators for the rules.
!            G(1,J),...,G(NDIM,J) are the generators for the points
!            associated with the the Jth weights.
!     ERRCOF Real array of dimension 6.
!            Heuristic error coefficients that are used in the
!            error estimation in BASRUL.
!            It is assumed that the error is computed using:
!             IF (N1*ERRCOF(1) < N2 and N2*ERRCOF(2) < N3)
!               THEN ERROR = ERRCOF(3)*N1
!               ELSE ERROR = ERRCOF(4)*MAX(N1, N2, N3)
!             ERROR = ERROR + EP*(ERRCOF(5)*ERROR/(ES+ERROR)+ERRCOF(6))
!            where N1-N3 are the null rules, EP is the error for
!            the parent
!            subregion and ES is the error for the sibling subregion.
!     SCALES Real array of dimension (3,WTLENG).
!            Scaling factors used to construct new null rules,
!            N1, N2 and N3,
!            based on a linear combination of two successive null rules
!            in the sequence of null rules.
!     NORMS  Real array of dimension (3,WTLENG).
!            2**NDIM/(1-norm of the null rule constructed by each of
!            the scaling factors.)

!***ROUTINES CALLED  D132RE, D113RE, D07HRE, D09HRE
!***END PROLOGUE DINHRE

!   Global variables.

INTEGER, INTENT(IN)    :: ndim, key, wtleng
REAL (dp), INTENT(OUT) :: g(:,:), w(:,:), errcof(:)
REAL (dp), INTENT(OUT) :: scales(:,:)
REAL (dp), INTENT(OUT) :: norms(:,:)

! Dimensions:
! g(ndim,wtleng), w(5,wtleng), errcof(6), scales(3,wtleng), norms(3,wtleng)

!   Local variables.

INTEGER   :: i, j, k
REAL (dp) :: we(14), rulpts(wtleng)

!***FIRST EXECUTABLE STATEMENT DINHRE

!   Compute W, G and ERRCOF.

IF (key == 1) THEN
  CALL d132re(w, g, errcof, rulpts)
ELSE IF (key == 2) THEN
  CALL d113re(w, g, errcof, rulpts)
ELSE IF (key == 3) THEN
  CALL d09hre(ndim, wtleng, w, g, errcof, rulpts)
ELSE IF (key == 4) THEN
  CALL d07hre(ndim, wtleng, w, g, errcof, rulpts)
END IF

!   Compute SCALES and NORMS.

DO k = 1, 3
  DO i = 1, wtleng
    IF (w(k+1,i) /= 0) THEN
      scales(k,i) = - w(k+2,i)/w(k+1,i)
    ELSE
      scales(k,i) = 100
    END IF
    DO j = 1, wtleng
      we(j) = w(k+2,j) + scales(k,i)*w(k+1,j)
    END DO
    norms(k,i) = 0
    DO j = 1, wtleng
      norms(k,i) = norms(k,i) + rulpts(j)*ABS(we(j))
    END DO
    norms(k,i) = 2**ndim/norms(k,i)
  END DO
END DO
RETURN

!***END DINHRE

END SUBROUTINE dinhre

SUBROUTINE d132re(w, g, errcof, rulpts)
!***BEGIN PROLOGUE D132RE
!***AUTHOR   Jarle Berntsen, EDB-senteret,
!            University of Bergen, Thormohlens gt. 55,
!            N-5008 Bergen, NORWAY
!***PURPOSE D132RE computes abscissas and weights of a 2 dimensional
!            integration rule of degree 13.
!            Two null rules of degree 11, one null rule of degree 9
!            and one null rule of degree 7 to be used in error
!            estimation are also computed.
! ***DESCRIPTION D132RE will select the correct values of the abscissas
!            and corresponding weights for different
!            integration rules and null rules and assign them to
!            G and W. The heuristic error coefficients ERRCOF
!            will also be assigned.

!   ON RETURN

!     W      Real array of dimension (5,WTLENG).
!            The weights for the basic and null rules.
!            W(1,1),...,W(1,WTLENG) are weights for the basic rule.
!            W(I,1),...,W(I,WTLENG), for I > 1 are null rule weights.
!     G      Real array of dimension (NDIM,WTLENG).
!            The fully symmetric sum generators for the rules.
!            G(1,J),...,G(NDIM,J) are the generators for the points
!            associated with the the Jth weights.
!     ERRCOF Real array of dimension 6.
!            Heuristic error coefficients that are used in the
!            error estimation in BASRUL.
!     RULPTS Real array of dimension WTLENG.
!            The number of points produced by each generator.
!***REFERENCES S.Eriksen,
!              Thesis of the degree cand.scient, Dept. of Informatics,
!              Univ. of Bergen, Norway, 1984.

!***ROUTINES CALLED-NONE
!***END PROLOGUE D132RE

!   Global variables

REAL (dp), INTENT(OUT) :: w(:,:), g(:,:), errcof(:)
REAL (dp), INTENT(OUT) :: rulpts(:)

! Dimensions:
! w(5,wtleng), g(2,wtleng), errcof(6), rulpts(wtleng)

!   Local variables.

INTEGER   :: i, j
REAL (dp), PARAMETER :: dim2g(16) = (/ 0.2517129343453109D+00,  &
    0.7013933644534266D+00, 0.9590960631619962D+00, 0.9956010478552127D+00, &
    0.5000000000000000D+00, 0.1594544658297559D+00, 0.3808991135940188D+00, &
    0.6582769255267192D+00, 0.8761473165029315D+00, 0.9982431840531980D+00, &
    0.9790222658168462D+00, 0.6492284325645389D+00, 0.8727421201131239D+00, &
    0.3582614645881228D+00, 0.5666666666666666D+00, 0.2077777777777778D+00 /)
REAL (dp), PARAMETER :: dim2w(14,5) = RESHAPE( (/   &
0.3379692360134460D-01,  &
0.9508589607597761D-01, 0.1176006468056962D+00,  &
0.2657774586326950D-01, 0.1701441770200640D-01,  &
0.0000000000000000D+00, 0.1626593098637410D-01,  &
0.1344892658526199D+00, 0.1328032165460149D+00,  &
0.5637474769991870D-01, 0.3908279081310500D-02,  &
0.3012798777432150D-01, 0.1030873234689166D+00, 0.6250000000000000D-01, &

0.3213775489050763D+00,  &
- .1767341636743844D+00, 0.7347600537466072D-01,  &
- .3638022004364754D-01, 0.2125297922098712D-01,  &
0.1460984204026913D+00, 0.1747613286152099D-01,  &
0.1444954045641582D+00, 0.1307687976001325D-03,  &
0.5380992313941161D-03, 0.1042259576889814D-03,  &
- .1401152865045733D-02, 0.8041788181514763D-02, - .1420416552759383D+00, &

0.3372900883288987D+00,  &
- .1644903060344491D+00, 0.7707849911634622D-01,  &
- .3804478358506310D-01, 0.2223559940380806D-01,  &
0.1480693879765931D+00, 0.4467143702185814D-05,  &
0.1508944767074130D+00, 0.3647200107516215D-04,  &
0.5777198999013880D-03, 0.1041757313688177D-03,  &
- .1452822267047819D-02, 0.8338339968783705D-02, - .1472796329231960D+00, &

- .8264123822525677D+00,  &
0.3065838614094360D+00, 0.2389292538329435D-02,  &
- .1343024157997222D+00, 0.8833366840533900D-01,  &
0.0000000000000000D+00, 0.9786283074168292D-03,  &
- .1319227889147519D+00, 0.7990012200150630D-02,  &
0.3391747079760626D-02, 0.2294915718283264D-02,  &
- .1358584986119197D-01, 0.4025866859057809D-01, 0.3760268580063992D-02, &

0.6539094339575232D+00,  &
- .2041614154424632D+00, - .1746981515794990D+00,  &
0.3937939671417803D-01, 0.6974520545933992D-02,  &
0.0000000000000000D+00, 0.6667702171778258D-02,  &
0.5512960621544304D-01, 0.5443846381278607D-01,  &
0.2310903863953934D-01, 0.1506937747477189D-01,  &
- .6057021648901890D-01, 0.4225737654686337D-01, 0.2561989142123099D-01 /), &
   (/ 14, 5 /))

!***FIRST EXECUTABLE STATEMENT D132RE

!   Assign values to W.

DO i = 1, 14
  DO j = 1, 5
    w(j,i) = dim2w(i,j)
  END DO
END DO

!   Assign values to G.

DO i = 1, 2
  DO j = 1, 14
    g(i,j) = 0.d0
  END DO
END DO
g(1,2) = dim2g(1)
g(1,3) = dim2g(2)
g(1,4) = dim2g(3)
g(1,5) = dim2g(4)
g(1,6) = dim2g(5)
g(1,7) = dim2g(6)
g(2,7) = g(1,7)
g(1,8) = dim2g(7)
g(2,8) = g(1,8)
g(1,9) = dim2g(8)
g(2,9) = g(1,9)
g(1,10) = dim2g(9)
g(2,10) = g(1,10)
g(1,11) = dim2g(10)
g(2,11) = g(1,11)
g(1,12) = dim2g(11)
g(2,12) = dim2g(12)
g(1,13) = dim2g(13)
g(2,13) = dim2g(14)
g(1,14) = dim2g(15)
g(2,14) = dim2g(16)

!   Assign values to RULPTS.

rulpts(1) = 1
DO i = 2, 11
  rulpts(i) = 4
END DO
rulpts(12) = 8
rulpts(13) = 8
rulpts(14) = 8

!   Assign values to ERRCOF.

errcof(1) = 10
errcof(2) = 10
errcof(3) = 1.
errcof(4) = 5.
errcof(5) = 0.5
errcof(6) = 0.25

!***END D132RE

RETURN
END SUBROUTINE d132re

SUBROUTINE d113re(w, g, errcof, rulpts)
!***BEGIN PROLOGUE D113RE
!***AUTHOR   Jarle Berntsen, EDB-senteret,
!            University of Bergen, Thormohlens gt. 55,
!            N-5008 Bergen, NORWAY
!***PURPOSE D113RE computes abscissas and weights of a 3 dimensional
!            integration rule of degree 11.
!            Two null rules of degree 9, one null rule of degree 7
!            and one null rule of degree 5 to be used in error
!            estimation are also computed.
!***DESCRIPTION D113RE will select the correct values of the abscissas
!            and corresponding weights for different
!            integration rules and null rules and assign them to G
!            and W.
!            The heuristic error coefficients ERRCOF
!            will also be computed.

!   ON RETURN

!     W      Real array of dimension (5,WTLENG).
!            The weights for the basic and null rules.
!            W(1,1),...,W(1,WTLENG) are weights for the basic rule.
!            W(I,1),...,W(I,WTLENG), for I > 1 are null rule weights.
!     G      Real array of dimension (NDIM,WTLENG).
!            The fully symmetric sum generators for the rules.
!            G(1,J),...,G(NDIM,J) are the generators for the points
!            associated with the the Jth weights.
!     ERRCOF Real array of dimension 6.
!            Heuristic error coefficients that are used in the
!            error estimation in BASRUL.
!     RULPTS Real array of dimension WTLENG.
!            The number of points used by each generator.

!***REFERENCES  J.Berntsen, Cautious adaptive numerical integration
!               over the 3-cube, Reports in Informatics 17, Dept. of
!               Inf.,Univ. of Bergen, Norway, 1985.
!               J.Berntsen and T.O.Espelid, On the construction of
!               higher degree three-dimensional embedded integration
!               rules, SIAM J. Numer. Anal.,Vol. 25,No. 1, pp.222-234,
!               1988.
!***ROUTINES CALLED-NONE
!***END PROLOGUE D113RE

!   Global variables.

REAL (dp), INTENT(OUT) :: w(:, :), g(:,:), errcof(:)
REAL (dp), INTENT(OUT) :: rulpts(:)

! Dimensions:
! w(5, wtleng), g(3,wtleng), errcof(6), rulpts(wtleng)

!   Local variables.

INTEGER   :: i
REAL (dp) :: dim3g(14) = (/ 0.1900000000000000D+00, 0.5000000000000000D+00,  &
    0.7500000000000000D+00, 0.8000000000000000D+00, 0.9949999999999999D+00,  &
    0.9987344998351400D+00, 0.7793703685672423D+00, 0.9999698993088767D+00,  &
    0.7902637224771788D+00, 0.4403396687650737D+00, 0.4378478459006862D+00,  &
    0.9549373822794593D+00, 0.9661093133630748D+00, 0.4577105877763134D+00 /)

REAL (dp), PARAMETER :: dim3w(13,5) = RESHAPE( (/  &
0.7923078151105734D-02,  &
0.6797177392788080D-01, 0.1086986538805825D-02,  &
0.1838633662212829D+00, 0.3362119777829031D-01,  &
0.1013751123334062D-01, 0.1687648683985235D-02,  &
0.1346468564512807D+00, 0.1750145884600386D-02,  &
0.7752336383837454D-01, 0.2461864902770251D+00,  &
0.6797944868483039D-01, 0.1419962823300713D-01,  &

0.1715006248224684D+01,  &
- .3755893815889209D+00, 0.1488632145140549D+00,  &
- .2497046640620823D+00, 0.1792501419135204D+00,  &
0.3446126758973890D-02, - .5140483185555825D-02,  &
0.6536017839876425D-02, - .6513454939229700D-03,  &
- .6304672433547204D-02, 0.1266959399788263D-01,  &
- .5454241018647931D-02, 0.4826995274768427D-02,  &

0.1936014978949526D+01,  &
- .3673449403754268D+00, 0.2929778657898176D-01,  &
- .1151883520260315D+00, 0.5086658220872218D-01,  &
0.4453911087786469D-01, - .2287828257125900D-01,  &
0.2908926216345833D-01, - .2898884350669207D-02,  &
- .2805963413307495D-01, 0.5638741361145884D-01,  &
- .2427469611942451D-01, 0.2148307034182882D-01,  &

0.5170828195605760D+00,  &
0.1445269144914044D-01, - .3601489663995932D+00,  &
0.3628307003418485D+00, 0.7148802650872729D-02,  &
- .9222852896022966D-01, 0.1719339732471725D-01,  &
- .1021416537460350D+00, - .7504397861080493D-02,  &
0.1648362537726711D-01, 0.5234610158469334D-01,  &
0.1445432331613066D-01, 0.3019236275367777D-02,  &

0.2054404503818520D+01,  &
0.1377759988490120D-01, - .5768062917904410D+00,  &
0.3726835047700328D-01, 0.6814878939777219D-02,  &
0.5723169733851849D-01, - .4493018743811285D-01,  &
0.2729236573866348D-01, 0.3547473950556990D-03,  &
0.1571366799739551D-01, 0.4990099219278567D-01,  &
0.1377915552666770D-01, 0.2878206423099872D-02 /), (/ 13, 5 /))

!***FIRST EXECUTABLE STATEMENT D113RE

!   Assign values to W.

DO i = 1, 13
  w(1:5,i) = dim3w(i,1:5)
END DO

!   Assign values to G.

g = 0._dp
g(1,2) = dim3g(1)
g(1,3) = dim3g(2)
g(1,4) = dim3g(3)
g(1,5) = dim3g(4)
g(1,6) = dim3g(5)
g(1,7) = dim3g(6)
g(2,7) = g(1,7)
g(1,8) = dim3g(7)
g(2,8) = g(1,8)
g(1,9) = dim3g(8)
g(2,9) = g(1,9)
g(3,9) = g(1,9)
g(1,10) = dim3g(9)
g(2,10) = g(1,10)
g(3,10) = g(1,10)
g(1,11) = dim3g(10)
g(2,11) = g(1,11)
g(3,11) = g(1,11)
g(1,12) = dim3g(12)
g(2,12) = dim3g(11)
g(3,12) = g(2,12)
g(1,13) = dim3g(13)
g(2,13) = g(1,13)
g(3,13) = dim3g(14)

!   Assign values to RULPTS.

rulpts(1) = 1
rulpts(2) = 6
rulpts(3) = 6
rulpts(4) = 6
rulpts(5) = 6
rulpts(6) = 6
rulpts(7) = 12
rulpts(8) = 12
rulpts(9) = 8
rulpts(10) = 8
rulpts(11) = 8
rulpts(12) = 24
rulpts(13) = 24

!   Assign values to ERRCOF.

errcof(1) = 4
errcof(2) = 4.
errcof(3) = 0.5
errcof(4) = 3.
errcof(5) = 0.5
errcof(6) = 0.25

!***END D113RE

RETURN
END SUBROUTINE d113re

SUBROUTINE d09hre(ndim, wtleng, w, g, errcof, rulpts)
!***BEGIN PROLOGUE D09HRE
!***KEYWORDS basic integration rule, degree 9
!***PURPOSE  To initialize a degree 9 basic rule and null rules.
!***AUTHOR   Alan Genz, Computer Science Department, Washington
!            State University, Pullman, WA 99163-1210 USA
!***LAST MODIFICATION 88-05-20
!***DESCRIPTION  D09HRE initializes a degree 9 integration rule,
!            two degree 7 null rules, one degree 5 null rule and one
!            degree 3 null rule for the hypercube [-1,1]**NDIM.

!   ON ENTRY

!   NDIM   Integer.
!          Number of variables.
!   WTLENG Integer.
!          The number of weights in each of the rules.

!   ON RETURN
!   W      Real array of dimension (5,WTLENG).
!          The weights for the basic and null rules.
!          W(1,1),...,W(1,WTLENG) are weights for the basic rule.
!          W(I,1),...,W(I,WTLENG), for I > 1 are null rule weights.
!   G      Real array of dimension (NDIM, WTLENG).
!          The fully symmetric sum generators for the rules.
!          G(1, J), ..., G(NDIM, J) are the are the generators for the
!          points associated with the Jth weights.
!   ERRCOF Real array of dimension 6.
!          Heuristic error coefficients that are used in the
!          error estimation in BASRUL.
!   RULPTS Real array of dimension WTLENG.
!          A work array.

!***REFERENCES A. Genz and A. Malik,
!             "An Imbedded Family of Fully Symmetric Numerical
!              Integration Rules",
!              SIAM J Numer. Anal. 20 (1983), pp. 580-588.
!***ROUTINES CALLED-NONE
!***END PROLOGUE D09HRE

!   Global variables

INTEGER, INTENT(IN)    :: ndim, wtleng
REAL (dp), INTENT(OUT) :: w(:,:), g(:,:), errcof(:)
REAL (dp), INTENT(OUT) :: rulpts(:)

! Dimensions:
! w(5,wtleng), g(ndim,wtleng), errcof(6), rulpts(wtleng)

!   Local Variables

REAL (dp) :: ratio, lam0, lam1, lam2, lam3, lamp, twondm
INTEGER :: i, j

!***FIRST EXECUTABLE STATEMENT D09HRE

!     Initialize generators, weights and RULPTS

DO j = 1, wtleng
  DO i = 1, ndim
    g(i,j) = 0
  END DO
  DO i = 1, 5
    w(i,j) = 0
  END DO
  rulpts(j) = 2*ndim
END DO
twondm = 2**ndim
rulpts(wtleng) = twondm
IF (ndim > 2) rulpts(8) = (4*ndim* (ndim-1)* (ndim-2))/3
rulpts(7) = 4*ndim* (ndim-1)
rulpts(6) = 2*ndim* (ndim-1)
rulpts(1) = 1

!     Compute squared generator parameters

lam0 = 0.4707
lam1 = 4/ (15-5/lam0)
ratio = (1-lam1/lam0)/27
lam2 = (5-7*lam1-35*ratio)/ (7-35*lam1/3-35*ratio/lam0)
ratio = ratio* (1-lam2/lam0)/3
lam3 = (7-9* (lam2+lam1)+63*lam2*lam1/5-63*ratio)/  &
       (9-63* (lam2+lam1)/5+21*lam2*lam1-63*ratio/lam0)
lamp = 0.0625

!     Compute degree 9 rule weights

w(1,wtleng) = 1/ (3*lam0)**4/twondm
IF (ndim > 2) w(1,8) = (1-1/ (3*lam0)) / (6*lam1)**3
w(1,7) = (1-7* (lam0+lam1)/5+7*lam0*lam1/3) /  &
         (84*lam1*lam2* (lam2-lam0)* (lam2-lam1))
w(1,6) = (1-7* (lam0+lam2)/5+7*lam0*lam2/3) /  &
         (84*lam1*lam1* (lam1-lam0)* (lam1-lam2)) - w(1,7)*lam2/lam1  &
         - 2* (ndim-2)*w(1,8)
w(1,4) = (1-9* ((lam0+lam1+lam2) /7 - (lam0*lam1+lam0*lam2+  &
         lam1*lam2)/5)-3*lam0*lam1*lam2)/  &
         (18*lam3* (lam3-lam0)* (lam3-lam1)* (lam3-lam2))
w(1,3) = (1-9* ((lam0+lam1+lam3)/7- (lam0*lam1+lam0*lam3+  &
         lam1*lam3)/5)-3*lam0*lam1*lam3)/  &
         (18*lam2* (lam2-lam0)* (lam2-lam1)* (lam2-lam3)) -2* (ndim-1)*w(1,7)
w(1,2) = (1-9* ((lam0+lam2+lam3)/7- (lam0*lam2+lam0*lam3+  &
         lam2*lam3)/5)-3*lam0*lam2*lam3) /  &
         (18*lam1* (lam1-lam0)* (lam1-lam2)* (lam1-lam3)) -  &
         2* (ndim-1)* (w(1,7)+w(1,6)+ (ndim-2)*w(1,8))

!     Compute weights for 2 degree 7, 1 degree 5 and 1 degree 3 rules

w(2,wtleng) = 1/ (108*lam0**4)/twondm
IF (ndim > 2) w(2,8) = (1-27*twondm*w(2,9)*lam0**3)/ (6*lam1)**3
w(2,7) = (1-5*lam1/3-15*twondm*w(2,wtleng)*lam0**2* (lam0-lam1))/  &
         (60*lam1*lam2* (lam2-lam1))
w(2,6) = (1-9* (8*lam1*lam2*w(2,7)+twondm*w(2,wtleng)*lam0**2))/  &
         (36*lam1*lam1) - 2*w(2,8)* (ndim-2)
w(2,4) = (1-7* ((lam1+lam2)/5-lam1*lam2/3+twondm*w(2,wtleng)*lam0*   &
         (lam0-lam1)* (lam0-lam2)))/(14*lam3* (lam3-lam1)* (lam3-lam2))
w(2,3) = (1-7* ((lam1+lam3)/5-lam1*lam3/3+twondm*w(2,wtleng)*lam0*   &
         (lam0-lam1)* (lam0-lam3)))/ (14*lam2* (lam2-lam1)* (lam2-lam3)) &
         - 2* (ndim-1)*w(2,7)
w(2,2) = (1-7* ((lam2+lam3)/5-lam2*lam3/3+twondm*w(2,wtleng)*lam0* &
         (lam0-lam2)* (lam0-lam3)))/ (14*lam1* (lam1-lam2)* (lam1-lam3)) -  &
         2* (ndim-1)* (w(2,7)+w(2,6)+ (ndim-2)*w(2,8))
w(3,wtleng) = 5/ (324*lam0**4)/twondm
IF (ndim > 2) w(3,8) = (1-27*twondm*w(3,9)*lam0**3)/ (6*lam1)**3
w(3,7) = (1-5*lam1/3-15*twondm*w(3,wtleng)*lam0**2* (lam0-lam1))/  &
         (60*lam1*lam2* (lam2-lam1))
w(3,6) = (1-9* (8*lam1*lam2*w(3,7)+twondm*w(3,wtleng)*lam0**2))/  &
         (36*lam1*lam1) - 2*w(3,8)* (ndim-2)
w(3,5) = (1-7* ((lam1+lam2)/5-lam1*lam2/3+twondm*w(3,wtleng)*lam0*  &
         (lam0-lam1)* (lam0-lam2)))/(14*lamp* (lamp-lam1)* (lamp-lam2))
w(3,3) = (1-7* ((lam1+lamp)/5-lam1*lamp/3+twondm*w(3,wtleng)*lam0*  &
         (lam0-lam1)* (lam0-lamp)))/  &
         (14*lam2* (lam2-lam1)* (lam2-lamp)) - 2* (ndim-1)*w(3,7)
w(3,2) = (1-7* ((lam2+lamp)/5-lam2*lamp/3+twondm*w(3,wtleng)*lam0*  &
         (lam0-lam2)* (lam0-lamp)))/ (14*lam1* (lam1-lam2)* (lam1-lamp)) -  &
         2* (ndim-1)* (w(3,7)+w(3,6)+ (ndim-2)*w(3,8))
w(4,wtleng) = 2/ (81*lam0**4)/twondm
IF (ndim > 2) w(4,8) = (2-27*twondm*w(4,9)*lam0**3)/ (6*lam1)**3
w(4,7) = (2-15*lam1/9-15*twondm*w(4,wtleng)*lam0* (lam0-lam1))/  &
         (60*lam1*lam2* (lam2-lam1))
w(4,6) = (1-9* (8*lam1*lam2*w(4,7)+twondm*w(4,wtleng)*lam0**2))/  &
         (36*lam1*lam1) - 2*w(4,8)* (ndim-2)
w(4,4) = (2-7* ((lam1+lam2)/5-lam1*lam2/3+twondm*w(4,wtleng)*lam0*  &
         (lam0-lam1)* (lam0-lam2)))/(14*lam3* (lam3-lam1)* (lam3-lam2))
w(4,3) = (2-7* ((lam1+lam3)/5-lam1*lam3/3+twondm*w(4,wtleng)*lam0*  &
         (lam0-lam1)* (lam0-lam3)))/  &
         (14*lam2* (lam2-lam1)* (lam2-lam3)) - 2* (ndim-1)*w(4,7)
w(4,2) = (2-7* ((lam2+lam3)/5-lam2*lam3/3+twondm*w(4,wtleng)*lam0*  &
         (lam0-lam2)* (lam0-lam3)))/ (14*lam1* (lam1-lam2)* (lam1-lam3)) -  &
         2* (ndim-1)* (w(4,7)+w(4,6)+ (ndim-2)*w(4,8))
w(5,2) = 1/ (6*lam1)

!     Set generator values

lam0 = SQRT(lam0)
lam1 = SQRT(lam1)
lam2 = SQRT(lam2)
lam3 = SQRT(lam3)
lamp = SQRT(lamp)
DO i = 1, ndim
  g(i,wtleng) = lam0
END DO
IF (ndim > 2) THEN
  g(1,8) = lam1
  g(2,8) = lam1
  g(3,8) = lam1
END IF
g(1,7) = lam1
g(2,7) = lam2
g(1,6) = lam1
g(2,6) = lam1
g(1,5) = lamp
g(1,4) = lam3
g(1,3) = lam2
g(1,2) = lam1

!     Compute final weight values.
!     The null rule weights are computed from differences between
!     the degree 9 rule weights and lower degree rule weights.

w(1,1) = twondm
DO j = 2, 5
  DO i = 2, wtleng
    w(j,i) = w(j,i) - w(1,i)
    w(j,1) = w(j,1) - rulpts(i)*w(j,i)
  END DO
END DO
DO i = 2, wtleng
  w(1,i) = twondm*w(1,i)
  w(1,1) = w(1,1) - rulpts(i)*w(1,i)
END DO

!     Set error coefficients

errcof(1) = 5
errcof(2) = 5
errcof(3) = 1.
errcof(4) = 5
errcof(5) = 0.5
errcof(6) = 0.25

!***END D09HRE

RETURN
END SUBROUTINE d09hre

SUBROUTINE d07hre(ndim, wtleng, w, g, errcof, rulpts)
!***BEGIN PROLOGUE D07HRE
!***KEYWORDS basic integration rule, degree 7
!***PURPOSE  To initialize a degree 7 basic rule, and null rules.
!***AUTHOR   Alan Genz, Computer Science Department, Washington
!            State University, Pullman, WA 99163-1210 USA
!***LAST MODIFICATION 88-05-31
!***DESCRIPTION  D07HRE initializes a degree 7 integration rule,
!            two degree 5 null rules, one degree 3 null rule and one
!            degree 1 null rule for the hypercube [-1,1]**NDIM.

!   ON ENTRY

!   NDIM   Integer.
!          Number of variables.
!   WTLENG Integer.
!          The number of weights in each of the rules.
!          WTLENG MUST be set equal to 6.

!   ON RETURN
!   W      Real array of dimension (5,WTLENG).
!          The weights for the basic and null rules.
!          W(1,1),...,W(1,WTLENG) are weights for the basic rule.
!          W(I,1),...,W(I,WTLENG), for I > 1 are null rule weights.
!   G      Real array of dimension (NDIM, WTLENG).
!          The fully symmetric sum generators for the rules.
!          G(1, J), ..., G(NDIM, J) are the are the generators for the
!          points associated with the Jth weights.
!   ERRCOF Real array of dimension 6.
!          Heuristic error coefficients that are used in the
!          error estimation in BASRUL.
!   RULPTS Real array of dimension WTLENG.
!          A work array.

!***REFERENCES A. Genz and A. Malik,
!             "An Imbedded Family of Fully Symmetric Numerical
!              Integration Rules",
!              SIAM J Numer. Anal. 20 (1983), pp. 580-588.
!***ROUTINES CALLED-NONE
!***END PROLOGUE D07HRE

!   Global variables

INTEGER, INTENT(IN)    :: ndim, wtleng
REAL (dp), INTENT(OUT) :: w(:,:), g(:,:), errcof(:)
REAL (dp), INTENT(OUT) :: rulpts(:)

! Dimensions:
! w(5,wtleng), g(ndim,wtleng), errcof(6), rulpts(wtleng)

!   Local Variables

REAL (dp) :: ratio, lam0, lam1, lam2, lamp, twondm
INTEGER   :: i, j

!***FIRST EXECUTABLE STATEMENT D07HRE

!     Initialize generators, weights and RULPTS

DO j = 1, wtleng
  DO i = 1, ndim
    g(i,j) = 0
  END DO
  DO i = 1, 5
    w(i,j) = 0
  END DO
  rulpts(j) = 2*ndim
END DO
twondm = 2**ndim
rulpts(wtleng) = twondm
rulpts(wtleng-1) = 2*ndim* (ndim-1)
rulpts(1) = 1

!     Compute squared generator parameters

lam0 = 0.4707
lamp = 0.5625
lam1 = 4/ (15-5/lam0)
ratio = (1-lam1/lam0)/27
lam2 = (5-7*lam1-35*ratio)/ (7-35*lam1/3-35*ratio/lam0)

!     Compute degree 7 rule weights

w(1,6) = 1/ (3*lam0)**3/twondm
w(1,5) = (1-5*lam0/3)/ (60* (lam1-lam0)*lam1**2)
w(1,3) = (1-5*lam2/3-5*twondm*w(1,6)*lam0* (lam0-lam2))/  &
         (10*lam1* (lam1-lam2)) - 2* (ndim-1)*w(1,5)
w(1,2) = (1-5*lam1/3-5*twondm*w(1,6)*lam0* (lam0-lam1))/(10*lam2* (lam2-lam1))

!     Compute weights for 2 degree 5, 1 degree 3 and 1 degree 1 rules

w(2,6) = 1/ (36*lam0**3)/twondm
w(2,5) = (1-9*twondm*w(2,6)*lam0**2)/ (36*lam1**2)
w(2,3) = (1-5*lam2/3-5*twondm*w(2,6)*lam0* (lam0-lam2))/  &
         (10*lam1* (lam1-lam2)) - 2* (ndim-1)*w(2,5)
w(2,2) = (1-5*lam1/3-5*twondm*w(2,6)*lam0* (lam0-lam1))/(10*lam2* (lam2-lam1))
w(3,6) = 5/ (108*lam0**3)/twondm
w(3,5) = (1-9*twondm*w(3,6)*lam0**2)/ (36*lam1**2)
w(3,3) = (1-5*lamp/3-5*twondm*w(3,6)*lam0* (lam0-lamp))/  &
         (10*lam1* (lam1-lamp)) - 2* (ndim-1)*w(3,5)
w(3,4) = (1-5*lam1/3-5*twondm*w(3,6)*lam0* (lam0-lam1))/(10*lamp* (lamp-lam1))
w(4,6) = 1/ (54*lam0**3)/twondm
w(4,5) = (1-18*twondm*w(4,6)*lam0**2)/ (72*lam1**2)
w(4,3) = (1-10*lam2/3-10*twondm*w(4,6)*lam0* (lam0-lam2))/  &
         (20*lam1* (lam1-lam2)) - 2* (ndim-1)*w(4,5)
w(4,2) = (1-10*lam1/3-10*twondm*w(4,6)*lam0* (lam0-lam1))/ (20*lam2* (lam2-lam1))

!     Set generator values

lam0 = SQRT(lam0)
lam1 = SQRT(lam1)
lam2 = SQRT(lam2)
lamp = SQRT(lamp)
DO i = 1, ndim
  g(i,wtleng) = lam0
END DO
g(1,wtleng-1) = lam1
g(2,wtleng-1) = lam1
g(1,wtleng-4) = lam2
g(1,wtleng-3) = lam1
g(1,wtleng-2) = lamp

!     Compute final weight values.
!     The null rule weights are computed from differences between
!     the degree 7 rule weights and lower degree rule weights.

w(1,1) = twondm
DO j = 2, 5
  DO i = 2, wtleng
    w(j,i) = w(j,i) - w(1,i)
    w(j,1) = w(j,1) - rulpts(i)*w(j,i)
  END DO
END DO
DO i = 2, wtleng
  w(1,i) = twondm*w(1,i)
  w(1,1) = w(1,1) - rulpts(i)*w(1,i)
END DO

!     Set error coefficients

errcof(1) = 5.
errcof(2) = 5.
errcof(3) = 1.
errcof(4) = 5.
errcof(5) = 0.5
errcof(6) = 0.25

!***END D07HRE

RETURN
END SUBROUTINE d07hre

SUBROUTINE drlhre(ndim, center, hwidth, wtleng, g, w, errcof, numfun,  &
                  funsub, scales, norms, basval, rgnerr, DIRECT)
!***BEGIN PROLOGUE DRLHRE
!***KEYWORDS basic numerical integration rule
!***PURPOSE  To compute basic integration rule values.
!***AUTHOR   Alan Genz,  Computer Science Department, Washington
!            State University, Pullman, WA 99163-1210 USA
!***LAST MODIFICATION 90-02-06
!***DESCRIPTION DRLHRE computes basic integration rule values for a
!            vector of integrands over a hyper-rectangular region.
!            These are estimates for the integrals. DRLHRE also computes
!            estimates for the errors and determines the coordinate axis
!            where the fourth difference for the integrands is largest.

!   ON ENTRY

!   NDIM   Integer.
!          Number of variables.
!   CENTER Real array of dimension NDIM.
!          The coordinates for the center of the region.
!   HWIDTH Real Array of dimension NDIM.
!          HWIDTH(I) is half of the width of dimension I of the region.
!   WTLENG Integer.
!          The number of weights in the basic integration rule.
!   G      Real array of dimension (NDIM,WTLENG).
!          The fully symmetric sum generators for the rules.
!          G(1,J), ..., G(NDIM,J) are the are the generators for the
!          points associated with the Jth weights.
!   W      Real array of dimension (5,WTLENG).
!          The weights for the basic and null rules.
!          W(1,1),...,W(1,WTLENG) are weights for the basic rule.
!          W(I,1),...,W(I,WTLENG), for I > 1 are null rule weights.
!   ERRCOF Real array of dimension 6.
!          The error coefficients for the rules.
!          It is assumed that the error is computed using:
!           IF (N1*ERRCOF(1) < N2 and N2*ERRCOF(2) < N3)
!             THEN ERROR = ERRCOF(3)*N1
!             ELSE ERROR = ERRCOF(4)*MAX(N1, N2, N3)
!           ERROR = ERROR + EP*(ERRCOF(5)*ERROR/(ES+ERROR)+ERRCOF(6))
!          where N1-N4 are the null rules, EP is the error
!          for the parent
!          subregion and ES is the error for the sibling subregion.
!   NUMFUN Integer.
!          Number of components for the vector integrand.
!   FUNSUB Externally declared subroutine.
!          For computing the components of the integrand at a point X.
!          It must have parameters (NDIM,X,NUMFUN,FUNVLS).
!           Input Parameters:
!            X      Real array of dimension NDIM.
!                   Defines the evaluation point.
!            NDIM   Integer.
!                   Number of variables for the integrand.
!            NUMFUN Integer.
!                   Number of components for the vector integrand.
!           Output Parameters:
!            FUNVLS Real array of dimension NUMFUN.
!                   The components of the integrand at the point X.
!   SCALES Real array of dimension (3,WTLENG).
!          Scaling factors used to construct new null rules based
!          on a linear combination of two successive null rules
!          in the sequence of null rules.
!   NORMS  Real array of dimension (3,WTLENG).
!          2**NDIM/(1-norm of the null rule constructed by each of the
!          scaling factors.)

!   ON RETURN

!   BASVAL Real array of dimension NUMFUN.
!          The values for the basic rule for each component
!          of the integrand.
!   RGNERR Real array of dimension NUMFUN.
!          The error estimates for each component of the integrand.
!   DIRECT Real.
!          The coordinate axis where the fourth difference of the
!          integrand values is largest.

!***REFERENCES
!   A.C.Genz and A.A.Malik, An adaptive algorithm for numerical
!   integration over an N-dimensional rectangular region,
!   J.Comp.Appl.Math., 6:295-302, 1980.

!   T.O.Espelid, Integration Rules, Null Rules and Error
!   Estimation, Reports in Informatics 33, Dept. of Informatics,
!   Univ. of Bergen, 1988.

!***ROUTINES CALLED: DFSHRE, FUNSUB

!***END PROLOGUE DRLHRE

!   Global variables.

INTEGER, INTENT(IN)       :: wtleng, numfun, ndim
REAL (dp), INTENT(IN)     :: center(:), hwidth(:), w(:,:), errcof(:),  &
                             scales(:,:), norms(:,:)
REAL (dp), INTENT(IN OUT) :: g(:,:)
REAL (dp), INTENT(OUT)    :: basval(:), rgnerr(:), DIRECT

! Dimensions:
! center(ndim), x(ndim), hwidth(ndim), basval(numfun), rgnerr(numfun)
! null(numfun,8), w(5,wtleng), g(ndim,wtleng), errcof(6), scales(3,wtleng)
! norms(3,wtleng)

INTERFACE
  SUBROUTINE funsub(ndim, z, nfun, f)
    IMPLICIT NONE
    INTEGER, PARAMETER     :: dp = SELECTED_REAL_KIND(15, 60)
    INTEGER, INTENT(IN)    :: ndim, nfun
    REAL (dp), INTENT(IN)  :: z(:)
    REAL (dp), INTENT(OUT) :: f(:)
  END SUBROUTINE funsub
END INTERFACE

!   Local variables.

REAL (dp) :: rgnvol, difsum, difmax, frthdf, null(numfun,8), x(ndim)
INTEGER   :: i, j, k, divaxn
REAL (dp) :: search, ratio

!***FIRST EXECUTABLE STATEMENT DRLHRE

!       Compute volume of subregion, initialize DIVAXN and rule sums;
!       compute fourth differences and new DIVAXN (RGNERR is used
!       for a work array here). The integrand values used for the
!       fourth divided differences are accumulated in rule arrays.

rgnvol = 1
divaxn = 1
DO i = 1, ndim
  rgnvol = rgnvol*hwidth(i)
  x(i) = center(i)
  IF (hwidth(i) > hwidth(divaxn)) divaxn = i
END DO
CALL funsub(ndim, x, numfun, rgnerr)
DO j = 1, numfun
  basval(j) = w(1,1)*rgnerr(j)
  DO k = 1, 4
    null(j,k) = w(k+1,1)*rgnerr(j)
  END DO
END DO
difmax = 0
ratio = (g(1,3)/g(1,2))**2
DO i = 1, ndim
  x(i) = center(i) - hwidth(i)*g(1,2)
  CALL funsub(ndim, x, numfun, null(:,5))
  x(i) = center(i) + hwidth(i)*g(1,2)
  CALL funsub(ndim, x, numfun, null(:,6))
  x(i) = center(i) - hwidth(i)*g(1,3)
  CALL funsub(ndim, x, numfun, null(:,7))
  x(i) = center(i) + hwidth(i)*g(1,3)
  CALL funsub(ndim, x, numfun, null(:,8))
  x(i) = center(i)
  difsum = 0
  DO j = 1, numfun
    frthdf = 2* (1-ratio)*rgnerr(j) - (null(j,7)+null(j,8)) +  &
             ratio* (null(j,5)+null(j,6))

!           Ignore differences below roundoff

    IF (rgnerr(j)+frthdf/4 /= rgnerr(j)) difsum = difsum + ABS(frthdf)
    DO k = 1, 4
      null(j,k) = null(j,k) + w(k+1,2)*(null(j,5)+null(j,6)) +  &
      w(k+1,3)* (null(j,7)+null(j,8))
    END DO
    basval(j) = basval(j) + w(1,2)* (null(j,5)+null(j,6)) +  &
    w(1,3)* (null(j,7)+null(j,8))
  END DO
  IF (difsum > difmax) THEN
    difmax = difsum
    divaxn = i
  END IF
END DO
DIRECT = divaxn

!    Finish computing the rule values.

DO i = 4, wtleng
  CALL dfshre(ndim, center, hwidth, x, g(:,i), numfun, funsub, rgnerr,  &
              null(:,5))
  DO j = 1, numfun
    basval(j) = basval(j) + w(1,i)*rgnerr(j)
    DO k = 1, 4
      null(j,k) = null(j,k) + w(k+1,i)*rgnerr(j)
    END DO
  END DO
END DO

!    Compute errors.

DO j = 1, numfun

!    We search for the null rule, in the linear space spanned by two
!    successive null rules in our sequence, which gives the greatest
!    error estimate among all normalized (1-norm) null rules in this
!    space.

  DO i = 1, 3
    search = 0
    DO k = 1, wtleng
      search = MAX(search, ABS(null(j,i+1)+scales(i,k)*null(j,i))*norms(i,k))
    END DO
    null(j,i) = search
  END DO
  IF (errcof(1)*null(j,1) <= null(j,2) .AND.  &
      errcof(2)*null(j,2) <= null(j,3)) THEN
    rgnerr(j) = errcof(3)*null(j,1)
  ELSE
    rgnerr(j) = errcof(4)*MAX(null(j,1), null(j,2), null(j,3))
  END IF
  rgnerr(j) = rgnvol*rgnerr(j)
  basval(j) = rgnvol*basval(j)
END DO

!***END DRLHRE

RETURN
END SUBROUTINE drlhre

SUBROUTINE dfshre(ndim, center, hwidth, x, g, numfun, funsub, fulsms, funvls)
!***BEGIN PROLOGUE DFSHRE
!***KEYWORDS fully symmetric sum
!***PURPOSE  To compute fully symmetric basic rule sums
!***AUTHOR   Alan Genz, Computer Science Department, Washington
!            State University, Pullman, WA 99163-1210 USA
!***LAST MODIFICATION 88-04-08
!***DESCRIPTION DFSHRE computes a fully symmetric sum for a vector
!            of integrand values over a hyper-rectangular region.
!            The sum is fully symmetric with respect to the center of
!            the region and is taken over all sign changes and
!            permutations of the generators for the sum.

!   ON ENTRY

!   NDIM   Integer.
!          Number of variables.
!   CENTER Real array of dimension NDIM.
!          The coordinates for the center of the region.
!   HWIDTH Real Array of dimension NDIM.
!          HWIDTH(I) is half of the width of dimension I of the region.
!   X      Real Array of dimension NDIM.
!          A work array.
!   G      Real Array of dimension NDIM.
!          The generators for the fully symmetric sum. These MUST BE
!          non-negative and non-increasing.
!   NUMFUN Integer.
!          Number of components for the vector integrand.
!   FUNSUB Externally declared subroutine.
!          For computing the components of the integrand at a point X.
!          It must have parameters (NDIM, X, NUMFUN, FUNVLS).
!           Input Parameters:
!            X      Real array of dimension NDIM.
!                   Defines the evaluation point.
!            NDIM   Integer.
!                   Number of variables for the integrand.
!            NUMFUN Integer.
!                   Number of components for the vector integrand.
!           Output Parameters:
!            FUNVLS Real array of dimension NUMFUN.
!                   The components of the integrand at the point X.
!   ON RETURN

!   FULSMS Real array of dimension NUMFUN.
!          The values for the fully symmetric sums for each component
!          of the integrand.
!   FUNVLS Real array of dimension NUMFUN.
!          A work array.

!***ROUTINES CALLED: FUNSUB

!***END PROLOGUE DFSHRE

!   Global variables.

INTEGER, INTENT(IN)       :: ndim, numfun
REAL (dp), INTENT(IN)     :: center(:), hwidth(:)
REAL (dp), INTENT(IN OUT) :: x(:), g(:)
REAL (dp), INTENT(OUT)    :: fulsms(:), funvls(:)

INTERFACE
  SUBROUTINE funsub(ndim, z, nfun, f)
    IMPLICIT NONE
    INTEGER, PARAMETER     :: dp = SELECTED_REAL_KIND(15, 60)
    INTEGER, INTENT(IN)    :: ndim, nfun
    REAL (dp), INTENT(IN)  :: z(:)
    REAL (dp), INTENT(OUT) :: f(:)
  END SUBROUTINE funsub
END INTERFACE

!   Local variables.

INTEGER   :: ixchng, lxchng, i, j, l
REAL (dp) :: gl, gi

!***FIRST EXECUTABLE STATEMENT DFSHRE

fulsms(1:numfun) = 0

!     Compute centrally symmetric sum for permutation of G

20 DO i = 1, ndim
  x(i) = center(i) + g(i)*hwidth(i)
END DO
40 CALL funsub(ndim, x, numfun, funvls)
DO j = 1, numfun
  fulsms(j) = fulsms(j) + funvls(j)
END DO
DO i = 1, ndim
  g(i) = - g(i)
  x(i) = center(i) + g(i)*hwidth(i)
  IF (g(i) < 0) GO TO 40
END DO

!       Find next distinct permutation of G and loop back for next sum.
!       Permutations are generated in reverse lexicographic order.

DO i = 2, ndim
  IF (g(i-1) > g(i)) THEN
    gi = g(i)
    ixchng = i - 1
    DO l = 1, (i-1)/2
      gl = g(l)
      g(l) = g(i-l)
      g(i-l) = gl
      IF (gl <= gi) ixchng = ixchng - 1
      IF (g(l) > gi) lxchng = l
    END DO
    IF (g(ixchng) <= gi) ixchng = lxchng
    g(i) = g(ixchng)
    g(ixchng) = gi
    GO TO 20
  END IF
END DO

!     Restore original order to generators

DO i = 1, ndim/2
  gi = g(i)
  g(i) = g(ndim-i+1)
  g(ndim-i+1) = gi
END DO

!***END DFSHRE

RETURN
END SUBROUTINE dfshre

SUBROUTINE dtrhre(dvflag, ndim, numfun, sbrgns, values, errors, centrs,  &
                  hwidts, greate, center, hwidth, dir)
!***BEGIN PROLOGUE DTRHRE
!***PURPOSE DTRHRE maintains a heap of subregions.
!***DESCRIPTION DTRHRE maintains a heap of subregions.
!            The subregions are ordered according to the size
!            of the greatest error estimates of each subregion(GREATE).

!   PARAMETERS

!     DVFLAG Integer.
!            If DVFLAG = 1, we remove the subregion with
!            greatest error from the heap.
!            If DVFLAG = 2, we insert a new subregion in the heap.
!     NDIM   Integer.
!            Number of variables.
!     NUMFUN Integer.
!            Number of components of the integral.
!     SBRGNS Integer.
!            Number of subregions in the heap.
!     VALUES Real array of dimension (NUMFUN,SBRGNS).
!            Used to store estimated values of the integrals
!            over the subregions.
!     ERRORS Real array of dimension (NUMFUN,SBRGNS).
!            Used to store the corresponding estimated errors.
!     CENTRS Real array of dimension (NDIM,SBRGNS).
!            Used to store the center limits of the stored subregions.
!     HWIDTS Real array of dimension (NDIM,SBRGNS).
!            Used to store the hwidth limits of the stored subregions.
!     GREATE Real array of dimension SBRGNS.
!            Used to store the greatest estimated errors in all subregions.
!     CENTER Real array of dimension NDIM.
!            Used as intermediate storage for the center of the subregion.
!     HWIDTH Real array of dimension NDIM.
!            Used as intermediate storage for the half width of the subregion.
!     DIR    Integer array of dimension SBRGNS.
!            DIR is used to store the directions for further subdivision.

!***ROUTINES CALLED-NONE
!***END PROLOGUE DTRHRE

!   Global variables.

INTEGER, INTENT(IN)       :: dvflag, ndim, numfun
INTEGER, INTENT(IN OUT)   :: sbrgns
REAL (dp), INTENT(IN OUT) :: values(:,:), errors(:,:)
REAL (dp), INTENT(IN OUT) :: centrs(:,:)
REAL (dp), INTENT(IN OUT) :: hwidts(:,:)
REAL (dp), INTENT(IN OUT) :: greate(:)
REAL (dp), INTENT(IN OUT) :: center(:), hwidth(:)
REAL (dp), INTENT(IN OUT) :: dir(:)

! Dimensions:
! values(numfun,*), errors(numfun,*), centrs(ndim,*), hwidts(ndim,*)
! center(ndim), hwidth(ndim)

!   Local variables.

!   GREAT  is used as intermediate storage for the greatest error of a
!          subregion.
!   DIRECT is used as intermediate storage for the direction of further
!          subdivision.
!   SUBRGN Position of child/parent subregion in the heap.
!   SUBTMP Position of parent/child subregion in the heap.

INTEGER   :: j, subrgn, subtmp
REAL (dp) :: great, DIRECT, error(numfun), value(numfun)

!***FIRST EXECUTABLE STATEMENT DTRHRE

!   Save values to be stored in their correct place in the heap.

great = greate(sbrgns)
DIRECT = dir(sbrgns)
DO j = 1, numfun
  error(j) = errors(j,sbrgns)
  value(j) = values(j,sbrgns)
END DO
DO j = 1, ndim
  center(j) = centrs(j,sbrgns)
  hwidth(j) = hwidts(j,sbrgns)
END DO

!    If DVFLAG = 1, we will remove the region
!    with greatest estimated error from the heap.

IF (dvflag == 1) THEN
  sbrgns = sbrgns - 1
  subrgn = 1
  20 subtmp = 2*subrgn
  IF (subtmp <= sbrgns) THEN
    IF (subtmp /= sbrgns) THEN

!   Find max. of left and right child.

      IF (greate(subtmp) < greate(subtmp+1)) THEN
        subtmp = subtmp + 1
      END IF
    END IF

!   Compare max.child with parent.
!   If parent is max., then done.

    IF (great < greate(subtmp)) THEN

!   Move the values at position subtmp up the heap.

      greate(subrgn) = greate(subtmp)
      DO j = 1, numfun
        errors(j,subrgn) = errors(j,subtmp)
        values(j,subrgn) = values(j,subtmp)
      END DO
      dir(subrgn) = dir(subtmp)
      DO j = 1, ndim
        centrs(j,subrgn) = centrs(j,subtmp)
        hwidts(j,subrgn) = hwidts(j,subtmp)
      END DO
      subrgn = subtmp
      GO TO 20
    END IF
  END IF
ELSE IF (dvflag == 2) THEN

!   If DVFLAG = 2, then insert new region in the heap.

  subrgn = sbrgns
  40 subtmp = subrgn/2
  IF (subtmp >= 1) THEN

!   Compare max.child with parent.
!   If parent is max, then done.

    IF (great > greate(subtmp)) THEN

!   Move the values at position subtmp down the heap.

      greate(subrgn) = greate(subtmp)
      DO j = 1, numfun
        errors(j,subrgn) = errors(j,subtmp)
        values(j,subrgn) = values(j,subtmp)
      END DO
      dir(subrgn) = dir(subtmp)
      DO j = 1, ndim
        centrs(j,subrgn) = centrs(j,subtmp)
        hwidts(j,subrgn) = hwidts(j,subtmp)
      END DO
      subrgn = subtmp
      GO TO 40
    END IF
  END IF
END IF

!    Insert the saved values in their correct places.

IF (sbrgns > 0) THEN
  greate(subrgn) = great
  DO j = 1, numfun
    errors(j,subrgn) = error(j)
    values(j,subrgn) = value(j)
  END DO
  dir(subrgn) = DIRECT
  DO j = 1, ndim
    centrs(j,subrgn) = center(j)
    hwidts(j,subrgn) = hwidth(j)
  END DO
END IF

!***END DTRHRE

RETURN
END SUBROUTINE dtrhre

END MODULE multidim_integrate


!*==idamax.f90  processed by SPAG 7.51RB at 20:37 on  3 Mar 2022
!> \brief \b IDAMAX
!
!  =========== DOCUMENTATION ===========
!
! Online html documentation available at
!            http://www.netlib.org/lapack/explore-html/
!
!  Definition:
!  ===========
!
!       INTEGER FUNCTION IDAMAX(N,DX,INCX)
!
!       .. Scalar Arguments ..
!       INTEGER INCX,N
!       ..
!       .. Array Arguments ..
!       DOUBLE PRECISION DX(*)
!       ..
!
!
!> \par Purpose:
!  =============
!>
!> \verbatim
!>
!>    IDAMAX finds the index of the first element having maximum absolute value.
!> \endverbatim
!
!  Arguments:
!  ==========
!
!> \param[in] N
!> \verbatim
!>          N is INTEGER
!>         number of elements in input vector(s)
!> \endverbatim
!>
!> \param[in] DX
!> \verbatim
!>          DX is DOUBLE PRECISION array, dimension ( 1 + ( N - 1 )*abs( INCX ) )
!> \endverbatim
!>
!> \param[in] INCX
!> \verbatim
!>          INCX is INTEGER
!>         storage spacing between elements of DX
!> \endverbatim
!
!  Authors:
!  ========
!
!> \author Univ. of Tennessee
!> \author Univ. of California Berkeley
!> \author Univ. of Colorado Denver
!> \author NAG Ltd.
!
!> \date November 2017
!
!> \ingroup aux_blas
!
!> \par Further Details:
!  =====================
!>
!> \verbatim
!>
!>     jack dongarra, linpack, 3/11/78.
!>     modified 3/93 to return if incx .le. 0.
!>     modified 12/3/93, array(1) declarations changed to array(*)
!> \endverbatim
!>
!  =====================================================================
      INTEGER FUNCTION IDAMAX(N,Dx,Incx)
      IMPLICIT NONE
!*--IDAMAX75
!
!  -- Reference BLAS level1 routine (version 3.8.0) --
!  -- Reference BLAS is a software package provided by Univ. of Tennessee,    --
!  -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
!     November 2017
!
!     .. Scalar Arguments ..
      INTEGER Incx , N
!     ..
!     .. Array Arguments ..
      DOUBLE PRECISION Dx(*)
!     ..
!
!  =====================================================================
!
!     .. Local Scalars ..
      DOUBLE PRECISION dmax
      INTEGER i , ix
!     ..
!     .. Intrinsic Functions ..
      INTRINSIC DABS
!     ..
      IDAMAX = 0
      IF ( N<1 .OR. Incx<=0 ) RETURN
      IDAMAX = 1
      IF ( N==1 ) RETURN
      IF ( Incx==1 ) THEN
!
!        code for increment equal to 1
!
         dmax = DABS(Dx(1))
         DO i = 2 , N
            IF ( DABS(Dx(i))>dmax ) THEN
               IDAMAX = i
               dmax = DABS(Dx(i))
            ENDIF
         ENDDO
      ELSE
!
!        code for increment not equal to 1
!
         ix = 1
         dmax = DABS(Dx(1))
         ix = ix + Incx
         DO i = 2 , N
            IF ( DABS(Dx(ix))>dmax ) THEN
               IDAMAX = i
               dmax = DABS(Dx(ix))
            ENDIF
            ix = ix + Incx
         ENDDO
      ENDIF
      END FUNCTION IDAMAX


      real*8 function bint(f,a,b,epslon,epsabs)
      implicit none
      integer itmax,n,iter,i
      parameter (itmax=30)
      real*8 ,intent(in):: a,b,epslon,epsabs
      real*8 ba,s0,s20,s,xstep,x,s1,s2
      real*8 f
      external f

      ba=b-a
      s0=ba*(f(a)+f(b))*.5d0
c      write(*,*)'bint ',a,b,f(a),f(b)
      s20=s0
      n=1
      iter=0
1     s=0.d0
      xstep=ba/n
      x=a-xstep*.5d0
      do 10 i=1,n
        x=x+xstep
        s=s+f(x)
10    continue
      s=s*xstep
      s1=(s+s0)*.5d0
      s2=(2.d0*s+s0)/3.d0
c     write(*,*)s2
      if(abs(s2-s20) .lt. max(epslon*abs(s2),epsabs)
     1   .or. iter .gt. itmax)then
        bint=s2
        if(iter .gt. itmax)then
          write(*,*)'BINT Convergence failed. ',s2,s20
        endif
        return
      endif
      n=n*2
      s0=s1
      s20=s2
      iter=iter+1
      go to 1
      end

      real*8 function rombint(f,a,b,epslon,epsabs)
      implicit none
      integer*4 itmax,n,iter,i,k
      parameter (itmax=30,k=5)
      real*8 a,b,epslon,epsabs,ba,s0,s20,s,xstep,x,s1,s2,
     $     h(itmax),y(itmax),ds2
      real*8 f
      external f
      ba=b-a
      s0=ba*(f(a)+f(b))*.5d0
      s20=s0
      h(1)=1.d0
      y(1)=s0
      n=1
      iter=2
1     s=0.d0
      xstep=ba/n
      x=a-xstep*.5d0
      do i=1,n
        x=x+xstep
        s=s+f(x)
      enddo
      s=s*xstep
      s1=(s+s0)*.5d0
      h(iter)=h(iter-1)*.25d0
      y(iter)=s1
      if(iter .lt. k)then
        s2=(2.d0*s+s0)/3.d0
        if(abs(s2-s20) .lt. max(epslon*abs(s2),epsabs))then
          rombint=s2
          return
        endif
        s20=s2
      else
        call polint(h(iter-k+1),y(iter-k+1),k,0.d0,s2,ds2)
c        write(*,*)iter,s2,ds2
        rombint=s2
        if(abs(ds2) .lt. max(epslon*abs(s2),epsabs))then
          return
        endif
        if(iter .ge. itmax)then
          write(*,*)'ROMBINT Convergence failed. '
          return
        endif
      endif
      s0=s1
      n=n*2
      iter=iter+1
      go to 1
      end

      SUBROUTINE polint(xa,ya,n,x,y,dy)
      implicit none
      INTEGER*4 n,NMAX
      PARAMETER (NMAX=10)
      REAL*8 dy,x,y,xa(n),ya(n)
c Given arrays xa and ya, each of length n, and given a value x, this routine returns a
c value y, and an error estimate dy. If P(x) is the polynomial of degree N − 1 such that
c P(xai) = yai, i = 1, . . . , n, then the returned value y = P(x).
      INTEGER*4 i,m,ns
      REAL*8 den,dif,dift,ho,hp,w,c(NMAX),d(NMAX)
      ns=1
      dif=abs(x-xa(1))
      do i=1,n
        dift=abs(x-xa(i))
        if(dift .lt. dif)then
          ns=i
          dif=dift
        endif
        c(i)=ya(i)
        d(i)=ya(i)
      enddo
      y=ya(ns)
      ns=ns-1
      do m=1,n-1
        do i=1,n-m
          ho=xa(i)-x
          hp=xa(i+m)-x
          w=c(i+1)-d(i)
          den=ho-hp
          if(den .eq. 0.d0)then
            write(*,*)'polint failure in polint.'
          endif
          den=w/den
          d(i)=hp*den
          c(i)=ho*den
        enddo
        if(2*ns .lt. n-m)then
          dy=c(ns+1)
        else
          dy=d(ns)
          ns=ns-1
        endif
        y=y+dy
      enddo
      return
      END


      include 'ttb/ttb_library.f'

      subroutine hypela2(d,g,e,de,s,t,dt,ngens,m,nn,kcus,matus,ndi,
     2             nshear,disp,dispt,coord,ffn,frotn,strechn,eigvn,ffn1,
     3                   frotn1,strechn1,eigvn1,ncrd,itel,ndeg,ndm,
     4                   nnode,jtype,lclass,ifr,ifu)

      ! HYPELA2 Nearly-Incompressible Neo-Hookean Material
      ! Formulation: Total Lagrange, Displacement and Herrmann Elements
      ! Updated Lagrange: Push Forward and transform to Jaumann Tangent
      ! Example for usage of Tensor Toolbox
      !
      ! Switch to Voigt Notation:
      ! - change commented Tensor Datatypes
      !
      ! Andreas Dutzler
      ! 2017-12-21
      ! Graz University of Technology

      use Tensor
      implicit none

      real*8 coord, d, de, disp, dispt, dt, e, eigvn, eigvn1, ffn, ffn1
      real*8 frotn, frotn1, g
      integer ifr, ifu, itel, jtype, kcus, lclass, matus, m, ncrd, ndeg
      integer ndi, ndm, ngens, nn, nnode, nshear
      real*8 s, strechn, strechn1, t

      dimension e(*),de(*),t(*),dt(*),g(*),d(ngens,*),s(*)
      dimension m(2),coord(ncrd,*),disp(ndeg,*),matus(2),
     *          dispt(ndeg,*),ffn(itel,3),frotn(itel,3),
     *          strechn(itel),eigvn(itel,*),ffn1(itel,3),
     *          frotn1(itel,3),strechn1(itel),eigvn1(itel,*),
     *          kcus(2),lclass(2)
      include 'concom'
      include 'creeps'

      type(Tensor2)  :: F1
      type(Tensor2s) :: E1

      ! voigt notation: change to type Tensor2s, Tensor4s
      type(Tensor2s) :: C1,S1,invC1,Eye
      type(Tensor4s) :: C4, I4, SdyaI

      real(kind=8) :: J,J_th,p,dpdJ,kappa,C10,alpha

      integer ndim

      ! dimension
      ndim = ndi+nshear

      ! material parameters
      C10 = 0.5
      kappa = 500.0
      alpha = 1.5d-4

      Eye = identity2(Eye)
      F1 = tensorstore(Eye)
      F1%ab(1:itel,1:3) = ffn1(1:itel,1:3)
      J = det(F1)
      J_th = (1+alpha*(t(1)+dt(1)))**3

      C1 = transpose(F1)*F1
      J = det(C1)**(1./2.)
      invC1 = inv(C1)

      ! u or u/p formulation
      if (ngens > ndim) then
       p = e(ngens)+de(ngens)
       dpdJ = 0.d0
      else
       p = kappa*(J/J_th-1)
       dpdJ = kappa/J_th
      end if

      ! pk2 stress
      S1 = 2.*C10 * J**(-2./3.) * dev(C1)*invC1 + p*J*invC1

      if (iupdat.eq.1) then
       S1 = piola(F1,S1)/J ! S1 = 1/J * F1*S1*transpose(F1)
      endif

      ! output as array
      s(1:ndim) = asarray( voigt(S1), ndim )

      ! material elasticity tensor
      I4 = invC1.cdya.invC1
      C4 = 2.*C10*J**(-2./3.)*2./3. * (tr(C1)*I4
     *    -(Eye.dya.invC1)-(invC1.dya.Eye)
     *    +tr(C1)/3.*(invC1.dya.invC1))
     *    +(p*J+dpdJ*J**2)*(invC1.dya.invC1)
     *    -2.*p*J*I4
      if (iupdat.eq.1) then
       C4 = piola(F1,C4)/detF1 + (S1.cdya.Eye)+(Eye.cdya.S1)
      endif

      ! output as array
      d(1:ndim,1:ndim) = asarray( voigt(C4), ndim, ndim )

      ! herrmann formulation
      if (iupdat.eq.1) then
        invC1 = Eye/J
      endif
      if (ngens > ndim) then
       s(ngens) = (J-J_th) - p*J_th**2/kappa
       d(ngens,1:ndim) = asarray( J*voigt(invC1), ndim)
       d(1:ndim,ngens) = d(ngens,1:ndim)
       d(ngens,ngens) = -J_th**2/kappa
       g(1:ndim) = 0.d0
       g(ngens) = -(1+2*p/kappa*J_th) * 3.*alpha*J_th**(2./3.)*dt(1)
      else
       g(1:ndim) = -kappa*J/J_th**2 * 3.*alpha*J_th**(2./3.)
     *              * J * asarray(voigt(invC1), ndim)*dt(1)
      endif

      return
      end


PROGRAM F003

    ! Copyright 2021 Melwyn Francis Carlo

    IMPLICIT NONE

    INTEGER, PARAMETER :: MAX_N   = 1000

    CHARACTER (LEN=10) :: SUM_VAL = "0000000001"

    CHARACTER (LEN=20) :: TEMP_STR
    CHARACTER (LEN=10) :: PRODUCT_VAL

    CHARACTER (LEN=10), DIMENSION(10) :: SUB_PRODUCT_SUM

    INTEGER :: I, J, K, L, CARRY_VAL, TEMP_VAL, SUB_PRODUCT, SUB_PRODUCT_LEN

    DO I = 2, MAX_N

        IF (MOD(I, 10) == 0) CYCLE

        PRODUCT_VAL = "0000000001"

        DO J = 1, I

            SUB_PRODUCT_SUM = "0000000000"

            DO K = 10, 1, -1
                SUB_PRODUCT     = (IACHAR(PRODUCT_VAL(K:K)) - IACHAR('0')) * I
                TEMP_STR        = ACHAR(0)
                WRITE(TEMP_STR, "(I0.10)") SUB_PRODUCT
                SUB_PRODUCT_LEN = LEN_TRIM(ADJUSTL(TEMP_STR))
                SUB_PRODUCT_SUM(K)(1:K) = TEMP_STR((SUB_PRODUCT_LEN-K+1):SUB_PRODUCT_LEN)
            END DO

            CARRY_VAL = 0

            DO L = 10, 1, -1

                TEMP_VAL  = IACHAR(SUB_PRODUCT_SUM(1)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(2)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(3)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(4)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(5)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(6)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(7)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(8)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(9)(L:L))                    &
                          + IACHAR(SUB_PRODUCT_SUM(10)(L:L))                   &
                          - (IACHAR('0') * 10)                                 &
                          + CARRY_VAL

                CARRY_VAL = INT(TEMP_VAL / 10)

                PRODUCT_VAL(L:L) = ACHAR(IACHAR('0') + MOD(TEMP_VAL, 10))

            END DO

        END DO

        CARRY_VAL = 0

        DO J = 10, 1, -1
            TEMP_VAL     = IACHAR(SUM_VAL(J:J)) + IACHAR(PRODUCT_VAL(J:J))     &
                         - IACHAR('0') - IACHAR('0')                           &
                         + CARRY_VAL
            CARRY_VAL    = INT(TEMP_VAL / 10)
            SUM_VAL(J:J) = ACHAR(IACHAR('0') + MOD(TEMP_VAL, 10))
        END DO

    END DO

    PRINT ('(A)'), SUM_VAL

END PROGRAM F003


      DOUBLE PRECISION FUNCTION sla_DRANRM (ANGLE)
*+
*     - - - - - - -
*      D R A N R M
*     - - - - - - -
*
*  Normalize angle into range 0-2 pi  (double precision)
*
*  Given:
*     ANGLE     dp      the angle in radians
*
*  The result is ANGLE expressed in the range 0-2 pi.
*
*  Last revision:   22 July 2004
*
*  Copyright P.T.Wallace.  All rights reserved.
*
*  License:
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program (see SLA_CONDITIONS); if not, write to the
*    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*    Boston, MA  02110-1301  USA
*
*-

      IMPLICIT NONE

      DOUBLE PRECISION ANGLE

      DOUBLE PRECISION D2PI
      PARAMETER (D2PI=6.283185307179586476925286766559D0)

      sla_DRANRM = MOD(ANGLE,D2PI)
      IF (sla_DRANRM.LT.0D0) sla_DRANRM = sla_DRANRM+D2PI

      END


!===============================================================================
program possion_pmf
!===============================================================================
! Return the value of the probability mass function for the Poisson distribution
! with parameter lambda at integer value k.

   use statistical, only: stat_poisson_pmf

   implicit none

   logical :: read_stdin = .false.
   real(8) :: lambda
   integer :: i, k, k2, iline = 1
   integer :: iostat = 0

   call get_args

   if (read_stdin) then
      do while (iostat == 0)
         read(*,*,iostat=iostat) lambda, k
         if (iostat < 0) exit
         if (iostat > 0) then
            write(0,'(a,i0.1,a)') 'poisson_pmf: Error: Problem reading lambda, k ' &
               // 'from line ', iline, ' of stdin'
            error stop
         endif
         write(*,*) k, stat_poisson_pmf(lambda, k)
      enddo
   else
      do i = k, k2
         write(*,*) i, stat_poisson_pmf(lambda, i)
      enddo
   endif

contains
   subroutine usage
      write(0,'(a)') &
         'Usage: possion_pmf [lambda] [k] (k2)', &
         '       OR', &
         '       poisson_pmf < (lambda,k on stdin)', &
         'Return the value of the pmf for the Poisson distribution with parameter', &
         'lambda at integer value k, and optionally between k and k2 inclusive.'
     error stop
   end subroutine usage

   subroutine get_args
     integer :: iarg, narg
     character(len=250) :: arg
     narg = command_argument_count()
     if (narg == 1 .or. narg > 3) call usage
     if (narg == 0) then
        read_stdin = .true.
     else
        call get_command_argument(1, arg)
        read(arg,*) lambda
        call get_command_argument(2, arg)
        read(arg,*) k
        if (narg == 3) then
           call get_command_argument(3, arg)
           read(arg,*) k2
        else
           k2 = k
        endif
     endif
   end subroutine get_args

end program possion_pmf
!-------------------------------------------------------------------------------


      module intmath
      implicit none

      interface ilog2
      ! log(x)/log(2)
      module procedure ilog2_8
      module procedure ilog2_4
      module procedure ilog2_2
      module procedure ilog2_1
      end interface ilog2

      interface i1log2
      ! log(x+1)/log(2) unless x=maxint, in which case log(x)/log(2)
      module procedure i1log2_8
      module procedure i1log2_4
      module procedure i1log2_2
      module procedure i1log2_1
      end interface i1log2

      contains

      ! ----------------------------------------------------------------

      function i1log2_8(ival)
      implicit none
      integer(kind=8), value :: ival
      integer(kind=8)::i1log2_8
      integer(kind=8), parameter :: one=1
      if(ival+one<ival) then
         i1log2_8=ilog2_8(ival)
      else
         i1log2_8=ilog2_8(ival+one)
      endif
      end function i1log2_8

      ! ----------------------------------------------------------------

      function i1log2_4(ival)
      implicit none
      integer(kind=4), value :: ival
      integer(kind=4)::i1log2_4
      integer(kind=4), parameter :: one=1
      if(ival+one<ival) then
         i1log2_4=ilog2_4(ival)
      else
         i1log2_4=ilog2_4(ival+one)
      endif
      end function i1log2_4

      ! ----------------------------------------------------------------

      function i1log2_2(ival)
      implicit none
      integer(kind=2), value :: ival
      integer(kind=2)::i1log2_2
      integer(kind=2), parameter :: one=1
      if(ival+one<ival) then
         i1log2_2=ilog2_2(ival)
      else
         i1log2_2=ilog2_2(ival+one)
      endif
      end function i1log2_2

      ! ----------------------------------------------------------------

      function i1log2_1(ival)
      implicit none
      integer(kind=1), value :: ival
      integer(kind=1)::i1log2_1
      integer(kind=1), parameter :: one=1
      if(ival+one<ival) then
         i1log2_1=ilog2_1(ival)
      else
         i1log2_1=ilog2_1(ival+one)
      endif
      end function i1log2_1

      ! ----------------------------------------------------------------

      function ilog2_8(i_in)
      implicit none
      integer(kind=8), value :: i_in
      integer(kind=8)::ilog2_8,i
      ilog2_8=0
      i=i_in
      if(i<=0) return
      if(iand(i,i-1)/=0) then
         !write(0,*) 'iand i-1'
         ilog2_8=1
      endif
      if(iand(i,Z'FFFFFFFF00000000')/=0) then
         ilog2_8=ilog2_8+32
         i=ishft(i,-32)
         !write(0,*) 'iand ffffffff',i,ilog2_8
      endif
      if(iand(i,Z'00000000FFFF0000')/=0) then
         ilog2_8=ilog2_8+16
         i=ishft(i,-16)
         !write(0,*) 'iand ffff' ,i,ilog2_8
      endif
      if(iand(i,Z'000000000000FF00')/=0) then
         ilog2_8=ilog2_8+8
         i=ishft(i,-8)
         !write(0,*) 'iand ff',i,ilog2_8
      endif
      if(iand(i,Z'00000000000000F0')/=0) then
         ilog2_8=ilog2_8+4
         i=ishft(i,-4)
         !write(0,*) 'iand f',i,ilog2_8
      endif
      if(iand(i,Z'000000000000000C')/=0) then
         ilog2_8=ilog2_8+2
         i=ishft(i,-2)
         !write(0,*) 'iand c',i,ilog2_8
      endif
      if(iand(i,Z'0000000000000002')/=0) then
         ilog2_8=ilog2_8+1
         i=ishft(i,-1)
         !write(0,*) 'iand 2',i,ilog2_8
      endif
      end function ilog2_8

      ! ----------------------------------------------------------------

      function ilog2_4(i_in)
      implicit none
      integer(kind=4), value :: i_in
      integer(kind=4)::ilog2_4,i
      ilog2_4=0
      i=i_in
      if(i<=0) return
      if(iand(i,i-1)/=0) then
         !write(0,*) 'iand i-1'
         ilog2_4=1
      endif
      if(iand(i,Z'FFFF0000')/=0) then
         ilog2_4=ilog2_4+16
         i=ishft(i,-16)
         !write(0,*) 'iand ffff' ,i,ilog2_4
      endif
      if(iand(i,Z'0000FF00')/=0) then
         ilog2_4=ilog2_4+8
         i=ishft(i,-8)
         !write(0,*) 'iand ff',i,ilog2_4
      endif
      if(iand(i,Z'000000F0')/=0) then
         ilog2_4=ilog2_4+4
         i=ishft(i,-4)
         !write(0,*) 'iand f',i,ilog2_4
      endif
      if(iand(i,Z'0000000C')/=0) then
         ilog2_4=ilog2_4+2
         i=ishft(i,-2)
         !write(0,*) 'iand c',i,ilog2_4
      endif
      if(iand(i,Z'00000002')/=0) then
         ilog2_4=ilog2_4+1
         i=ishft(i,-1)
         !write(0,*) 'iand 2',i,ilog2_4
      endif
      end function ilog2_4

      ! ----------------------------------------------------------------

      function ilog2_2(i_in)
      implicit none
      integer(kind=2), value :: i_in
      integer(kind=2)::ilog2_2,i
      ilog2_2=0
      i=i_in
      if(i<=0) return
! WPS modification for the XL compiler
!      if(iand(i,i-1)/=0) then
      if(iand(i,i-1_2)/=0) then
         !write(0,*) 'iand i-1'
         ilog2_2=1
      endif
      if(iand(i,Z'FF00')/=0) then
         ilog2_2=ilog2_2+8
         i=ishft(i,-8)
         !write(0,*) 'iand ff',i,ilog2_2
      endif
      if(iand(i,Z'00F0')/=0) then
         ilog2_2=ilog2_2+4
         i=ishft(i,-4)
         !write(0,*) 'iand f',i,ilog2_2
      endif
      if(iand(i,Z'000C')/=0) then
         ilog2_2=ilog2_2+2
         i=ishft(i,-2)
         !write(0,*) 'iand c',i,ilog2_2
      endif
      if(iand(i,Z'0002')/=0) then
         ilog2_2=ilog2_2+1
         i=ishft(i,-1)
         !write(0,*) 'iand 2',i,ilog2_2
      endif
      end function ilog2_2

      ! ----------------------------------------------------------------

      function ilog2_1(i_in)
      implicit none
      integer(kind=1), value :: i_in
      integer(kind=1)::ilog2_1,i
      ilog2_1=0
      i=i_in
      if(i<=0) return
! WPS modification for the XL compiler
!      if(iand(i,i-1)/=0) then
      if(iand(i,i-1_1)/=0) then
         !write(0,*) 'iand i-1'
         ilog2_1=1
      endif
      if(iand(i,Z'F0')/=0) then
         ilog2_1=ilog2_1+4
         i=ishft(i,-4)
         !write(0,*) 'iand f',i,ilog2_1
      endif
      if(iand(i,Z'0C')/=0) then
         ilog2_1=ilog2_1+2
         i=ishft(i,-2)
         !write(0,*) 'iand c',i,ilog2_1
      endif
      if(iand(i,Z'02')/=0) then
         ilog2_1=ilog2_1+1
         i=ishft(i,-1)
         !write(0,*) 'iand 2',i,ilog2_1
      endif
      end function ilog2_1
      end module intmath
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
c$$$ TEST PROGRAM FOR THIS MODULE
c$$$      program test_intmath
c$$$      use intmath
c$$$      implicit none
c$$$      real(kind=16) :: temp
c$$$      real(kind=16), parameter :: alog2=log(2.0_16)
c$$$      integer(kind=8), parameter  ::                                    &
c$$$     &     one=1,big=Z'7FFFFFFFFFFFFFFF',small=-2000000_8,              &
c$$$     &     check=Z'1FFFFFFF'
c$$$      integer(kind=8) :: ival, iret
c$$$      !$OMP PARALLEL DO PRIVATE(ival,temp,iret)
c$$$      do ival=small,big
c$$$ 10      format(Z16,' -- MISMATCH: ',I0,'=>',I0,' (',I0,' = ',F0.10,')')
c$$$ 20      format(Z16,' -- OKAY:     ',I0,'=>',I0,' (',I0,' = ',F0.10,')')
c$$$         if(ival+one<ival) then
c$$$            temp=log(real(max(ival,one),kind=16))/alog2
c$$$         else
c$$$            temp=log(real(max(ival+one,one),kind=16))/alog2
c$$$         endif
c$$$         iret=i1log2(ival)
c$$$         if(iret/=ceiling(temp) .or. ival==0 .or. ival==check) then
c$$$            !$OMP CRITICAL
c$$$            if(iret/=ceiling(temp)) then
c$$$               print 10, ival, ival, iret,ceiling(temp),temp
c$$$            else
c$$$               print 20, ival, ival, iret,ceiling(temp),temp
c$$$            endif
c$$$            !$OMP END CRITICAL
c$$$         endif
c$$$      enddo
c$$$      !$OMP END PARALLEL DO
c$$$      end program test_intmath