      subroutine smpint ( nd, nf, mincls, maxcls, funsub, epsabs, 
     &  epsrel, key, sbrgns, wrklen, vrtwrk, restar, value, error, 
     &  funcls, inform )

c*********************************************************************72
c
cc SMPINT integrates a vector function over a collection of simplexes.
c
c***BEGIN PROLOGUE SMPINT
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***KEYWORDS automatic multidimensional integrator,
c            n-dimensional simplex, general purpose, global adaptive
c***PURPOSE  To calculate an approximation to a vector of integrals
c
c               I = I (F ,F ,...,F   ) DS
c                    S  1  2      NF
c
c            where S is a collection ND-dimensional simplices,
c            and  F = F (X ,X ,...,X  ), K = 1, 2, ..., NF,
c                  K   K  1  2      ND
c            and try to satisfy for each component I(K) of I
c              ABS( I(K) - VALUE(K) ) < MAX( EPSABS, EPSREL*ABS(I(K)) )
c
c***DESCRIPTION Computation of integrals over simplical regions.
c            SMPINT is a driver for the integration routine SMPSAD,
c            which repeatedly subdivides the region of integration and
c            estimates the integrals and the errors over the subregions
c            with greatest estimated errors until the error request
c            is met or MAXCLS function evaluations have been used.
c
c   ON ENTRY
c
c     ND     Integer, number of variables. 1 < ND
c     NF     Integer, number of components of the integral.
c     MINCLS Integer, minimum number of FUNSUB calls.
c     MAXCLS Integer, maximum number of FUNSUB calls.
c            RULCLS is number FUNSUB calls for each subregion (see WRKLEN),
c            DIFCLS = 1 + 2*ND*( ND + 1 ).
c            If RESTAR = 0, MAXCLS must be >= MAX(SBRGNS*RULCLS,MINCLS).
c            If RESTAR = 1, MAXCLS must be >= MAX(4*RULCLS+DIFCLS,MINCLS).
c     FUNSUB Externally declared subroutine for computing components of
c            the integrand at the given evaluation point.
c            It must have parameters (ND,X,NF,FUNVLS)
c            Input parameters:
c              ND   Integer that gives the dimension of I
c              X      Real array of dimension ND that contains the
c                     evaluation point.
c              NF Integer that gives the number of components of I.
c            Output parameter:
c              FUNVLS Real array of dimension NF that contains the
c                     components of the integrand.
c     EPSABS Real.
c            Requested absolute accuracy.
c     EPSREL Real requested relative accuracy.
c     KEY    Integer, key to selected local integration rule.
c            KEY = 3 gives the user a (default) degree 7 integration rule.
c            KEY = 1 gives the user a degree 3 integration rule.
c            KEY = 2 gives the user a degree 5 integration rule.
c            KEY = 3 gives the user a degree 7 integration rule.
c            KEY = 4 gives the user a degree 9 integration rule.
c     WRKLEN Integer, length of the working array VRTWRK.
c             WRKLEN should be >= WRKSBS*( ND*(ND+1) + 2*NF + 3 )
c                                + (ND+1)*(ND+2) + 7*NF,      where
c             WRKSBS = SBRGNS + 3*( MAXCLS/RULCLS - SBRGNS*(1-RESTAR) )/4.
c            If
c              KEY = 0, RULCLS = (ND+4)*(ND+3)*(ND+2)/6 + (ND+2)*(ND+1);
c              KEY = 1, RULCLS = 2*ND+3;
c              KEY = 2, RULCLS = (ND+3)*(ND+2)/2 + 2*(ND+1);
c              KEY = 3, RULCLS = (ND+4)*(ND+3)*(ND+2)/6 + (ND+2)*(ND+1);
c              KEY = 4, RULCLS = (ND+5)*(ND+4)*(ND+3)*(ND+2)/24
c                                 + 5*(ND+2)*(ND+1)/2 .
c     SBRGNS Integer, initial number of simplices.
c     VRTWRK Real array of dimension WRKLEN.
c            Work should contain the simplex vertices for SBRGNS
c            simplices; the coordinates of vertex J for simplex K
c            must be in VRTWRK(I+J*ND+(K-1)*ND*(ND+1)),
c            for I = 1, ..., ND; J = 0, ..., ND; K = 1, ..., SBRGNS.
c            The rest of VRTWRK is used as working storage; see below.
c     RESTAR Integer.
c            If RESTAR = 0, this is the first attempt to compute
c             the integrals over SBRGNS simplices.
c            If RESTAR = 1, then we restart a previous attempt.
c             In this case the only parameters for SMPINT that may
c             be changed (with respect to the previous call of SMPINT)
c             are MINCLS, MAXCLS, EPSABS, EPSREL, KEY and RESTAR.
c   ON RETURN
c
c     SBRGNS Integer.
c            SBRGNS contains the current number of simplices. They
c            were obtained by subdividing the input simplicies.
c     VRTWRK   Real array of dimension WRKLEN.
c            Used as working storage.
c            Let MAXSUB = (WRKLEN-(ND+1)*(ND+2)-7*NF)/(ND*(ND+1)+2*NF+3).
c            VRTWRK(1), ..., VRTWRK(ND*(ND+1)), ...,
c             VRTWRK(ND*(ND+1)*SBRGNS) contain subregion vertices.
c            VRTWRK(ND*(ND+1)*MAXSUB+1), ...,
c             VRTWRK(ND*(ND+1)*MAXSUB+NF*SBRGNS) contain
c             estimated components of the integrals over the subregions.
c            VRTWRK(ND*(ND+1)*MAXSUB+NF*MAXSUB+1), ...,
c             VRTWRK(ND*(ND+1)*MAXSUB+NF*(MAXSUB+SBRGNS))
c             contain estimated errors for the subregions.
c            VRTWRK(ND*(ND+1)*MAXSUB+2*NF*MAXSUB+1), ...,
c             VRTWRK(ND*(ND+1)*MAXSUB+2*NF*MAXSUB+SBRGNS))
c             contain volumes of the subregions (scaled by ND!).
c            VRTWRK(ND*(ND+1)*MAXSUB+(2*NF+1)*MAXSUB+1), ...,
c             VRTWRK(ND*(ND+1)*MAXSUB+(2*NF+1)*MAXSUB+SBRGNS)
c             contain greatest errors in each subregion.
c            VRTWRK(ND*(ND+1)*MAXSUB+(2*NF+2)*MAXSUB+1), ...,
c             VRTWRK(ND*(ND+1)*MAXSUB+(2*NF+2)*MAXSUB+SBRGNS)
c             contain pointers for the subregion heap.
c            The rest of VRTWRK is used as temporary storage in SMPSAD.
c     VALUE  Real array of dimension NF of integral approximations.
c     ERROR  Real array of dimension NF, of absolute accuracy estimates.
c     FUNCLS Integer, number of FUNSUB calls used by SMPINT.
c     INFORM Integer.
c            INFORM = 0 for normal exit, when ERROR(K) <=  EPSABS or
c              ERROR(K) <=  ABS(VALUE(K))*EPSREL with MAXCLS or less
c              function evaluations for all values of K, 1 <= K <= NF.
c            INFORM = 1 if MAXCLS was too small for SMPINT to obtain
c              the required accuracy. In this case SMPINT returns
c              values VALUE with estimated absolute accuracies ERROR.
c            INFORM = 2 if KEY < 0 or KEY > 4,
c            INFORM = 3 if ND < 2,
c            INFORM = 4 if NF < 1,
c            INFORM = 5 if EPSABS < 0 and EPSREL < 0,
c            INFORM = 6 if WRKLEN is too small,
c            INFORM = 7 if RESTAR < 0 or RESTAR > 1,
c            INFORM = 8 if SBRGNS <= 0.
c
c***ROUTINES CALLED SMPCHC,SMPSAD
c***END PROLOGUE SMPINT
c
c   Global variables.
c
      external funsub
      integer nd, nf, mincls, maxcls, sbrgns
      integer key, wrklen, restar, funcls, inform
      double precision epsabs, epsrel
      double precision value(nf), error(nf), vrtwrk(wrklen)
c
c   Local variables.
c
c   MAXSUB Integer, maximum allowed number of subdivisions
c          for the given values of KEY, ND and NF.
c
      integer maxsub, rulcls, i1, i2, i3, i4, i5, i6
c
c***FIRST PROCESSING STATEMENT SMPINT
c
c  Compute MAXSUB and RULCLS, and check the input parameters.
c
c
      call smpchc ( nd, nf, mincls, maxcls, epsabs, epsrel, sbrgns,
     &  key, wrklen, restar, rulcls, maxsub, inform )

      if ( inform .eq. 0 ) then
c
c  Split up the work space and call SMPSAD.
c
         i1 =  1 + maxsub*nd*(nd+1)
         i2 = i1 + maxsub*nf
         i3 = i2 + maxsub*nf
         i4 = i3 + maxsub
         i5 = i4 + maxsub
         i6 = i5 + maxsub
         call smpsad( nd, nf, funsub, mincls, maxcls, epsabs, epsrel,
     &        restar, key, rulcls, maxsub, sbrgns, vrtwrk,
     &        vrtwrk(i1), vrtwrk(i2), vrtwrk(i3), vrtwrk(i4),
     &        vrtwrk(i5), vrtwrk(i6), value, error, funcls, inform )
      else
         funcls = 0
      end if

      return
      end
      subroutine smpchc ( nd, nf, mincls, maxcls, epsabs, epsrel, 
     &  sbrgns, key, wrklen, restar, rulcls, maxsub, inform )

c*********************************************************************72
c
cc SMPCHC checks the input parameters to SMPINT.
c
c***BEGIN PROLOGUE SMPCHC
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***PURPOSE  SMPCHC checks validity of input parameters for SMPINT.
c***DESCRIPTION
c            SMPCHC computes MAXSUB, RULCLS and INFORM as functions of
c             input parameters for SMPINT, and checks the validity of
c             input parameters for SMPINT.
c
c   ON ENTRY
c
c     ND   Integer, number of variables,  ND > 1.
c     NF Integer, number of components of the integral.
c     MINCLS Integer, minimum number of new FUNSUB calls.
c     MAXCLS Integer, maximum number of new FUNSUB calls.
c            The number of function values for each subregion is RULCLS.
c            If
c             KEY = 0, RULCLS = (ND+4)*(ND+3)*(ND+2)/6 + (ND+2)*(ND+1);
c             KEY = 1, RULCLS = 2*ND+3;
c             KEY = 2, RULCLS = (ND+3)*(ND+2)/2 + 2*(ND+1);
c             KEY = 3, RULCLS = (ND+4)*(ND+3)*(ND+2)/6 + (ND+2)*(ND+1);
c             KEY = 4, RULCLS = (ND+5)*(ND+4)*(ND+3)*(ND+2)/24
c                               + 5*(ND+2)*(ND+1)/2 .
c            DIFCLS = 1 + 2*ND*( ND + 1 ).
c            If RESTAR = 0, MAXCLS must be >= MAX(SBRGNS*RULCLS,MINCLS).
c            If RESTAR = 1, MAXCLS must be >= MAX(4*RULCLS+DIFCLS,MINCLS).
c     EPSABS Real, requested absolute accuracy.
c     EPSREL Real, requested relative accuracy.
c     SBRGNS Integer, initial number of simplices.
c     KEY    Integer, key to selected local integration rule.
c            KEY = 0 gives the user a (default)degree 7 integration rule.
c            KEY = 1 gives the user a degree 3 integration rule.
c            KEY = 2 gives the user a degree 5 integration rule.
c            KEY = 3 gives the user a degree 7 integration rule.
c            KEY = 4 gives the user a degree 9 integration rule.
c     WRKLEN Integer, length of the working array WORK.
c             WRKLEN should be >= WRKSBS*( ND*(ND+1) + 2*NF + 3 )
c                                + (ND+1)*(ND+2) + 7*NF, where
c             WRKSBS = SBRGNS + 3*( MAXCLS/RULCLS - SBRGNS*(1-RESTAR) )/4.
c     RESTAR Integer.
c            If RESTAR = 0, this is the first attempt to compute
c             the integral over the SBRGNS input simplices.
c            If RESTAR = 1, then we restart a previous attempt.
c
c   ON RETURN
c
c     RULCLS Integer, number of function values for each subregion.
c            If
c             KEY = 0, RULCLS = (ND+4)*(ND+3)*(ND+2)/6 + (ND+2)*(ND+1);
c             KEY = 1, RULCLS = 2*ND+3;
c             KEY = 2, RULCLS = (ND+3)*(ND+2)/2 + 2*(ND+1);
c             KEY = 3, RULCLS = (ND+4)*(ND+3)*(ND+2)/6 + (ND+2)*(ND+1);
c             KEY = 4, RULCLS = (ND+5)*(ND+4)*(ND+3)*(ND+2)/24
c                               + 5*(ND+2)*(ND+1)/2 .
c     MAXSUB Integer, maximum allowed number of subregions for the
c            given values of MAXCLS, WRKLEN, KEY and ND.
c     INFORM Integer.
c            INFORM = 0 for normal exit,
c            INFORM = 2 if KEY < 0 or KEY > 4,
c            INFORM = 3 if ND < 2,
c            INFORM = 4 if NF < 1,
c            INFORM = 5 if EPSABS < 0 and EPSREL < 0.,
c            INFORM = 6 if WRKLEN is too small,
c            INFORM = 7 if RESTAR < 0 or RESTAR > 1,
c            INFORM = 8 if SBRGNS <= 0.
c
c***END PROLOGUE SMPCHC
c
c   Global variables.
c
      integer nd, nf, mincls, maxcls, key, maxsub
      integer wrklen, inform, restar, rulcls, sbrgns
      double precision epsabs, epsrel
c
c     Local variables.
c
      integer wrkdif, difcls
c
c***FIRST PROCESSING STATEMENT SMPCHC
c
      inform = 0
c
c  Check valid KEY.
c
      if ( key .lt. 0 .or. key .gt. 4 ) inform = 2
c
c  Check valid ND.
c
      if ( nd .lt. 2 ) inform = 3
c
c  Check positive NF.
c
      if ( nf .lt. 1 ) inform = 4
c
c  Check valid accuracy requests.
c
      if ( epsabs .lt. 0 .and. epsrel .lt. 0 ) inform = 5
c
c  Check workspace.
c
      wrkdif = (nd+1)*(nd+2) + 7*nf
      maxsub = ( wrklen - wrkdif )/( (nd+1)*nd + 2*nf + 3 )
      if ( maxsub .le. sbrgns ) inform = 6
c
c  Check valid RESTAR.
c
      if ( restar .ne. 0 .and. restar .ne. 1 ) inform = 7
c
c  Check valid SBRGNS.
c
      if ( sbrgns .le. 0 ) inform = 8
c
c  Compute RULCLS as a function of KEY and ND and check MAXCLS.
c
      if ( inform .eq. 0 ) then
         difcls = 1 + 2*nd*( nd + 1 )
         if (key .eq. 0) rulcls = (nd+4)*(nd+3)*(nd+2)/6 + (nd+2)*(nd+1)
         if (key .eq. 1) rulcls = 2*nd + 3
         if (key .eq. 2) rulcls = (nd+3)*(nd+2)/2 + 2*(nd+1)
         if (key .eq. 3) rulcls = (nd+4)*(nd+3)*(nd+2)/6 + (nd+2)*(nd+1)
         if (key .eq. 4) rulcls = (nd+5)*(nd+4)*(nd+3)*(nd+2)/24
     &                           + 5*(nd+2)*(nd+1)/2
         if ( restar.eq.0 .and. maxcls.lt.max(sbrgns*rulcls,mincls) .or.
     &        restar.eq.1 .and. maxcls.lt.max(4*rulcls+difcls,mincls) )
     &        inform = 1
      end if

      return
      end
      subroutine smpsad ( nd, nf, funsub, mincls, maxcls, epsabs, 
     &  epsrel, restar, key, rulcls, maxsub, sbrgns, vertcs, values, 
     &  errors, volums, greats, pontrs, work, value, error, funcls, 
     &  inform )

c*********************************************************************72
c
cc SMPSAD integrates a vector function over a hyperrectangle.
c
c***BEGIN PROLOGUE SMPSAD
c***KEYWORDS automatic multidimensional integrator,
c            n-dimensional simplex,
c            general purpose, global adaptive
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***PURPOSE  The routine calculates an approximation to a given
c            vector of definite integrals, I, over a hyper-rectangular
c            region hopefully satisfying for each component of I the
c            following claim for accuracy:
c            ABS( I(K) - VALUE(K) ) .LE. MAX( EPSABS, EPSREL*ABS(I(K) ) )
c***DESCRIPTION Computation of integrals over hyper-rectangular regions.
c            SMPSAD repeatedly subdivides the regions of integration
c            and estimates the integrals and the errors over the
c            subregions with  greatest estimated errors until the error
c            request is met or MAXSUB subregions are stored. The regions
c            are divided into three or four equally sized parts along
c            the direction(s) with greatest absolute fourth difference.
c
c   ON ENTRY
c
c     ND     Integer, number of variables, ND > 1.
c     NF     Integer, number of components of the integral.
c     FUNSUB Externally declared subroutine for computing components of
c            the integrand at the given evaluation point.
c            It must have parameters (ND,X,NF,FUNVLS)
c            Input parameters:
c              ND Integer that gives the dimension of I
c              X  Real array of dimension ND that contains the
c                     evaluation point.
c              NF Integer that gives the number of components of I.
c            Output parameter:
c              FUNVLS Real array of dimension NF that contains the
c                     components of the integrand.
c     MINCLS Integer.
c            The computations proceed until there are at least
c            MINCLS FUNSUB calls.
c     MAXCLS Integer.
c            The computations proceed until further subdivision would
c            require more than MAXCLS FUNSUB calls. When RESTAR = 1,
c            this is the number of new FUNSUB calls.
c     EPSABS Real, requested absolute accuracy.
c     EPSREL Real, requested relative accuracy.
c     RESTAR Integer.
c            If RESTAR = 0, this is the first attempt to compute
c             the integral.
c            If RESTAR = 1, then we restart a previous attempt.
c             (In this case the output parameters from SMPSAD
c             must not be changed since the last exit from SMPSAD.)
c     KEY    Integer, key to selected local integration rule.
c            KEY = 1 gives the user a degree 3 integration rule.
c            KEY = 2 gives the user a degree 5 integration rule.
c            KEY = 3 gives the user a degree 7 integration rule.
c            KEY = 4 gives the user a degree 9 integration rule.
c     RULCLS Integer, number of FUNSUB calls needed for each subregion.
c     MAXSUB Integer; computations proceed until there are at most
c            MAXSUB subregions in the data structure.
c     SBRGNS Integer.
c            If RESTAR = 0, then SBRGNS must specify the number
c            of subregions stored in a previous call to SMPSAD.
c     VERTCS Real array of dimension (ND,0:ND,*).
c            Simplex vertices for each subregion; for subregion K vertex
c            J must have components VERTEX(I,J,K), I = 1, 2, ..., ND.
c     VALUES Real array of dimension (NF,*), for estimated values of the
c             integrals over the subregions.
c     ERRORS Real array of dimension (NF,*).
c            Used to store the corresponding estimated errors.
c            Used to store the half widths of the stored subregions.
c     GREATS Real array of dimension (*).
c            Used to store the greatest estimated errors in subregions.
c     PONTRS Real array of dimension (*), for the pointers from the
c             subregion heap to the actual subregions.
c     WORK   Real array, used in SMPVOL, SMPDFS, and SMPRUL.
c
c   ON RETURN
c
c     SBRGNS Integer, number of stored subregions.
c     VALUE  Real array of dimension NF.
c            Approximations to all components of the integral.
c     ERROR  Real array of dimension NF.
c            Estimates of absolute accuracies.
c     FUNCLS Integer, number of new FUNSUB calls used by SMPSAD.
c     INFORM Integer.
c            INFORM = 0 for normal exit, when ERROR(K) <=  EPSABS or
c              ERROR(K) <=  ABS(VALUE(K))*EPSREL, 1 <= K <= NF,
c              with MAXSUB or fewer subregions processed.
c            INFORM = 1 if MAXSUB was too small for SMPSAD
c              to obtain the required accuracy. In this case SMPSAD
c              returns values of VALUE with estimated absolute
c              accuracies ERROR.
c
c***REFERENCES
c***ROUTINES CALLED SMPSTR, SMPVOL, SMPRUL
c***END PROLOGUE SMPSAD
c
c   Global variables.
c
      external funsub
      integer nd, nf, rulcls, mincls, maxcls, maxsub, key, restar
      integer funcls, sbrgns, inform
      double precision epsabs, epsrel, value(nf), error(nf)
      double precision values(nf,*), errors(nf,*), vertcs(nd,0:nd,*)
      double precision volums(*), greats(*), pontrs(*), work(*)
c
c   Local variables.
c
c
c   MXNWSB is the maxiumum number of new subregions per subdivision.
c
      integer i, index, j, top, mxnwsb, newsbs, dfcost
      parameter ( mxnwsb = 4 )
      double precision smpvol, tune
      parameter( tune = 1 )
c
c***FIRST PROCESSING STATEMENT SMPSAD
c
c
c  Initialize for rule parameters.
c
      funcls = 0
      dfcost = 1 + 2*nd*( nd + 1 )
c
c  If RESTAR = 0, initialize for first call.
c
      if ( restar .eq. 0 ) then
c
c  Initialize FUNCLS, and VALUE and ERROR arrays.
c
         do j = 1, nf
            value(j) = 0
            error(j) = 0
         end do
         do index = 1, sbrgns
c
c  Call SMPVOL to compute the simplex volume(s).
c
            volums(index) = smpvol( nd, vertcs(1,0,index), work )
c
c  Apply basic rule over each simplex.
c
            call smprul( tune, nd, vertcs(1,0,index), volums(index),
     &           nf, funsub, key, values(1,index), errors(1,index),
     &           greats(index), work, work(2*nd+2) )
c
c  Add new contributions to VALUE and ERROR.
c  Store results in heap.
c
            do j = 1, nf
               value(j) = value(j) + values(j,index)
               error(j) = error(j) + errors(j,index)
            end do
            call smpstr( index, index, pontrs, greats )
            funcls = funcls + rulcls
         end do
      end if
      inform = max( 0, min( mincls - funcls, 1 ) )
      do j = 1, nf
         if( error(j) .gt. max(epsabs,epsrel*abs(value(j))) ) inform = 1
      end do
c
c  End initialisation.
c
      do while ( inform .gt. 0 .and. sbrgns + mxnwsb - 1 .le. maxsub
     &           .and. funcls + dfcost + mxnwsb*rulcls .le. maxcls )
c
c  Begin loop while error is too large, and FUNCLS and SBRGNS
c  are not too large.
c
c  Adjust VALUE and ERROR.
c
         top = int ( pontrs(1) )
         do j = 1, nf
            value(j) = value(j) - values(j,top)
            error(j) = error(j) - errors(j,top)
         end do
c
c  Determine NEWSBS new subregions.
c
         call smpdfs( nd, nf, funsub, top, sbrgns, vertcs,
     &                volums, work, work(nd+1), work(2*nd+1),
     &                work(3*nd+1), work(3*nd+1+5*nf), newsbs )
c
c  Apply basic rule, store results in heap and
c  add new contributions to VALUE and ERROR.
c
         index = top
         do i = 1, newsbs
            call smprul( tune, nd, vertcs(1,0,index), volums(index), nf,
     &                   funsub, key, values(1,index), errors(1,index),
     &                   greats(index), work, work(2*nd+2) )
            call smpstr( index, sbrgns+i-1, pontrs, greats )
            do j = 1, nf
               value(j) = value(j) + values(j,index)
               error(j) = error(j) + errors(j,index)
            end do
            index = sbrgns + i
         end do
         funcls = funcls + dfcost + newsbs*rulcls
         sbrgns = sbrgns + newsbs - 1
c
c  Check for error termination.
c
         inform = max( 0, min( mincls - funcls, 1 ) )
         do j = 1, nf
            if( error(j) .gt. max(epsabs,epsrel*abs(value(j))) )
     &           inform = 1
         end do
      end do
c
c  Compute more accurate values of VALUE and ERROR.
c
      do i = 1, nf
         value(i) = 0
         error(i) = 0
         do j = 1, sbrgns
            value(i) = value(i) + values(i,j)
            error(i) = error(i) + errors(i,j)
         end do
      end do

      return
      end
      function smpvol ( nd, vertex, work )

c*********************************************************************72
c
cc SMPVOL computes the scaled volume of a simplex.
c
c  Discussion:
c
c    This routine computes the volume of an ND-simplex scaled by 
c    using Gauss elimination to compute a determinant.
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c  Parameters:
c
c    Input, integer ND, the number of variables.
c
c    Input, double precision VERTEX(ND,0:ND), the simplex vertices;
c    The simplex vertex J must have components VERTEX(I,J), I = 1, 2, ..., ND.
c
c    Input, double precision WORK(ND,ND).
c
c    Output, double precision SMPVOL, the volume.
c
      implicit none

      integer nd

      integer i
      integer j
      integer k
      double precision mult
      integer pivpos
      double precision smpvol
      double precision vertex(nd,0:nd)
      double precision vol
      double precision work(nd,nd)
      double precision wtemp
c
c  Copy vertex differences to WORK array.
c
      do j = 1, nd
        do i = 1, nd
          work(i,j) = vertex(i,j) - vertex(i,0)
        end do
      end do
c
c  Use Gauss elimination with partial pivoting.
c
      vol = 1

      do k = 1, nd

        pivpos = k
        do j = k+1, nd
          if ( abs ( work(k,pivpos) ) .lt. abs ( work(k,j) ) ) then
            pivpos = j
          end if
        end do

        do i = k, nd
          wtemp = work(i,k)
          work(i,k) = work(i,pivpos)
          work(i,pivpos) = wtemp
        end do

        vol = vol * work(k,k) / k

        do j = k+1, nd
          mult = work(k,j) / work(k,k)
          do i = k+1, nd
            work(i,j) = work(i,j) - mult * work(i,k)
          end do
        end do

      end do

      smpvol = abs ( vol )

      return
      end
      subroutine smprul ( tune, nd, vertex, volume, nf, intgnd,
     &  inkey, basval, rgnerr, great, gt, rule )

c*********************************************************************72
c
cc SMPRUL computes the integration rules.
c
c***BEGIN PROLOGUE SMPRUL
c***KEYWORDS basic numerical integration rule
c***PURPOSE  To compute basic integration rule values.
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***DESCRIPTION SMPRUL computes basic integration rule values for a
c            vector of integrands over a hyper-rectangular region.
c            These are estimates for the integrals. SMPRUL also computes
c            estimates for the errors.
c
c   ON ENTRY
c
c     TUNE   Real, tuning parameter, with 0 <= TUNE <= 1, with
c            TUNE = 1 for the most conservative error estimates.
c            If TUNE < 0, only the rule parameters are computed.
c     ND    Integer, number of variables.
c     VERTEX Real array of dimension (ND,0:ND).
c            The simplex vertices; vertex J must have components
c            VERTEX(I,J), I = 1, 2, ..., ND.
c     NF     Integer, number of components for the vector integrand.
c     INTGND Subroutine for computing components of the integrand at Z.
c            It must have parameters (ND,X,NF,FUNVLS)
c            Input parameters:
c              ND    Integer that gives the dimension.
c              X      Real array of dimension ND that contains the
c                     evaluation point.
c              NF     Integer that gives the number of components of I.
c            Output parameter:
c              FUNVLS Real array of dimension NF that contains the
c                     components of the integrand.
c     INKEY  Integer rule parameter.
c            If INKEY .GT. 0 and INKEY .LT. 5 then a rule of degree
c            2*INKEY + 1; otherwise default degree 7 rule is used.
c     GT     Real work array of length 2*ND+1.
c     RULE   Real work array of dimension (NF,7).
c
c   ON RETURN
c
c     BASVAL Real array of length NF, values for the basic rule for
c            each component of the integrand.
c     RGNERR Real array of length NF, error estimates for BASVAL.
c     GREAT  Real, maximum component of RGNERR.
c
c
c***ROUTINES CALLED: SMPRMS, SYMRUL
c
c***END PROLOGUE SMPRUL
c
c   Global variables.
c
      external intgnd
      integer nf, nd, inkey
      double precision vertex(nd,0:nd), basval(nf), rgnerr(nf)
      double precision volume, tune, great
c
c   Local variables.
c
c   WTS    Integer number of weights in the integration rules.
c   W      Real array of dimension (WTS,RLS).
c          The weights for the basic and null rules.
c          W(1,1),...,W(WTS,1) are weights for the basic rule.
c          W(1,I),...,W(WTS,I), for I > 1 are null rule weights.
c   G      Real array of dimension (0:4, WTS).
c          The fully symmetric sum generators for the rules.
c
      integer key, rls, wts, mxw, mxrls, mxg
      parameter( mxw = 21, mxrls = 7, mxg = 4  )
      double precision w( mxw, mxrls ), g( 0:mxg, mxw ), wtsum
      double precision gt( 0:2*nd ), rule( nf, mxrls )
      double precision normcf, normnl, normcp, alpha(mxrls)
      double precision ratio, errcof, ratmin, small, smprod
      parameter( ratmin = 1d-1, small = 1d-12 )
      integer i, j, k, oldkey, oldn, pts(mxw)
      save oldkey, oldn, key, pts, w, g, rls, wts
      data oldkey, oldn/ -1, 0 /
c
c***FIRST PROCESSING STATEMENT SMPRUL
c
      if ( oldkey .ne. inkey .or. oldn .ne. nd ) then
         oldn = nd
         oldkey = inkey
         if ( inkey .gt. 0 .and. inkey .lt. 5 ) then
            key = inkey
         else
            key = 3
         end if
c
c  Compute WTS, RLS, weights, generators, ERRCOF and PTS.
c
         call smprms( nd, key, mxw, w, mxg, g, wts, rls, pts )
c
c  Orthogonalize and normalize null rules.
c
         normcf = smprod( wts, pts, w(1,1), w(1,1) )
         do k = 2, rls
            do j = 2, k-1
               alpha(j) = -smprod( wts, pts, w(1,j), w(1,k) )
            end do
            do i = 1, wts
               wtsum = 0
               do j = 2, k-1
                  wtsum = wtsum + w(i,j)*alpha(j)
               end do
               w(i,k) = w(i,k) + wtsum/normcf
            end do
            normnl = smprod( wts, pts, w(1,k), w(1,k) )
            do i = 1, wts
               w(i,k) = w(i,k)*sqrt( normcf/normnl )
            end do
         end do
      end if
      if ( tune .ge. 0 ) then
c
c  Compute the rule values.
c
         do i = 1, nf
            do j = 1, rls
               rule(i,j) = 0
            end do
         end do
         do k = 1, wts
            if ( pts(k) .gt. 0 ) then
               do i = 0, min(nd,mxg-1)
                  gt(i) = g(i,k)
               end do
               if ( nd .ge. mxg ) call smpcpy( mxg, nd, gt, g(mxg,k) )
               call smpsms( nd, vertex, nf, intgnd, gt, basval,
     &                                              gt(nd+1), rgnerr )
               do j = 1, rls
                  do i = 1, nf
                     rule(i,j) = rule(i,j) + w(k,j)*basval(i)
                  end do
               end do
            end if
         end do
c
c  Scale integral values and compute the error estimates.
c
         errcof = ( 8*tune + ( 1 - tune ) )
         great = 0
         do i = 1, nf
            basval(i) = rule(i,1)
            normcf = abs( basval(i) )
            rgnerr(i) = 0
            ratio = ratmin
            do k = rls, 3, -2
               normnl = max( abs( rule(i,k) ), abs( rule(i,k-1) ) )
               if ( normnl .gt. small*normcf .and. k .lt. rls )
     &              ratio = max( normnl/normcp, ratio )
               rgnerr(i) = max( normnl, rgnerr(i) )
               normcp = normnl
            end do
            if( ratio .ge. 1 ) then
               rgnerr(i) = tune*rgnerr(i) + ( 1 - tune )*normcp
            else if ( key .gt. 1 ) then
               rgnerr(i) = ratio*normcp
            end if
            rgnerr(i) = volume*max( errcof*rgnerr(i), small*normcf )
            basval(i) = volume*basval(i)
            great = max( great, rgnerr(i) )
         end do
      end if

      return
      end
      function smprod ( n, w, x, y )

c*********************************************************************72
c
cc SMPROD computes a weighted dot product.
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
      integer n, i, w(*)
      double precision smprod
      double precision x(*), y(*), sum

      sum = 0.0D+00
      do i = 1, n
         sum = sum + w(i)*x(i)*y(i)
      end do
      smprod = sum

      return
      end
      subroutine smprms ( nd, key, mxw, w, mxg, g, wts, rls, pts )

c*********************************************************************72
c
cc SMPRMS initializes a basic rule and some null rules.
c
c***BEGIN PROLOGUE SMPRMS
c***KEYWORDS basic integration rule, degree 2*KEY+1
c***PURPOSE  To initialize a degree 2*KEY+1 basic rule and null rules.
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***DESCRIPTION  SMPRMS initializes a degree 2*KEY+1 rule, and
c                and max(2*KEY,2) lower degree null rules.
c
c   ON ENTRY
c
c   ND    Integer, number of variables.
c   KEY    Integer, < 5 and >= 0, rule parameter.
c          If KEY > 0 a degree 2*KEY+1 rule is initialized.
c          If KEY = 0 a degree 7 rule is initialized.
c
c   ON RETURN
c   RLS    Integer, total number of rules.
c   WTS    Integer, total number of weights in each of the rules.
c   W      Real array of dimension (MXW,*).
c          The weights for the basic and null rules.
c          W(1,1),...,W(WTS,1) are weights for the basic rule.
c          W(I,1),...,W(WTS,I) for I .GT. 1 are null rule weights.
c   G      Real array of dimension (0:MXG,MXW).
c          The fully symmetric sum generators for the rules.
c          G(0,J), ..., G(MXG,J) are the generators for the
c          points associated with the Jth weights.
c   PTS    Integer array of length (MXW). PTS(J) is number of integrand
c          values needed for generator J.
c
c***REFERENCES
c
c    Axel Grundmann, Michael Moeller,
c    Invariant Integration Formulas for the N-Simplex
c    by Combinatorial Methods,
c    SIAM Journal on Numerical Analysis,
c    Volume 15, Number 2, April 1978, pages 282-290.
c
c    Arthur Stroud,
c    A Fifth Degree Integration Formula for the n-Simplex,
c    SIAM Journal on Numerical Analysis,
c    Volume 6, Number 1, March 1969, pages 90-98.
c
c    Ivan Mysovskikh,
c    On a cubature formula for the simplex,
c    Voprosy Vycislitelnoj i Prikl. Mat.,
c    Volume 51, 1978, pages 74-90.
c
c***END PROLOGUE SMPRMS
c
c   Global variables
c
      integer nd, key, wts, mxw, rls, mxg
      integer pts(mxw)
      double precision w(mxw,*), g(0:mxg,*)
c
c   Local Variables
c
      double precision one, ffteen
      parameter( one = 1, ffteen = 15 )
      double precision dr, dr2, dr4, dr6, dr8
      double precision r1, s1, r2, s2, u1, v1, u2, v2, l1, l2, d1, d2
      double precision a1, a2, a3, p0, p1, p2, p3, u5, u6, u7, sg
      double precision r, a, p, q, th, tp
      integer iw, gms, i, j
c
c***FIRST PROCESSING STATEMENT SMPRMS
c
c
c     Initialize RLS and GMS.
c
      if ( key .eq. 1 ) then
         rls = 3
         gms = 2
         wts = 3
      else if ( key .eq. 2 ) then
         rls = 5
         gms = 4
         wts = 6
      else if ( key .eq. 3 .or. key .eq. 0 ) then
         rls = 7
         gms = 7
         wts = 11
      else if ( key .eq. 4 ) then
         rls = 7
         if ( nd .eq. 2 ) then
            gms = 11
            wts = 20
         else
            gms = 12
            wts = 21
         end if
      end if
c
c  Initialize generators, weights and PTS.
c
      do i = 1, wts
         do j = 1, rls
            w(i,j) = 0
         end do
         pts(i) = 0
      end do
c
c  Compute generator, PTS and weight values for all rules.
c
      dr = nd
      dr2 =     ( dr + 1 )*( dr + 2 )
      dr4 = dr2*( dr + 3 )*( dr + 4 )
      dr6 = dr4*( dr + 5 )*( dr + 6 )
      dr8 = dr6*( dr + 7 )*( dr + 8 )
      call smpcpy( 0, mxg, g(0,1), 1/( dr + 1 ) )
      pts(1) = 1
      r1 = ( dr + 4 - sqrt(ffteen) )/( dr*dr + 8*dr + 1 )
      s1 = 1 - dr*r1
      l1 = s1 - r1
      g(0   ,gms+1) = s1
      call smpcpy( 1, mxg, g(0,gms+1), r1 )
      do i = 1, mxg
         g(i,gms+1) = r1
      end do
      pts(gms+1) = int ( dr + 1 )
      iw = rls
      if ( key .lt. 4 )  then
c
c  Compute weights for special degree 1 rule.
c
         w(1,iw) = 1
         iw = iw - 1
         w(gms+1,iw) = 1/( dr + 1 )
         iw = iw - 1
      end if
c
c  Compute weights, generators and PTS for degree 3 rule.
c
      g(0,2) = 3/( dr + 3 )
      call smpcpy( 1, mxg, g(0,2), 1/( dr + 3 ) )
      pts(2) = int ( dr + 1 )
        w(2,iw) = ( dr + 3 )**3/( 4*dr2*( dr + 3 ) )
      if ( key .gt. 1 ) then
         iw = iw - 1
c
c  Compute weights, generators and PTS for degree 3 and 5 rules.
c
         if ( nd .eq. 2 ) then
c
c  Special degree 3 rule.
c
            l2 = .62054648267200632589046034361711d0
            l1 = -sqrt( one/2 - l2**2 )
            r1 = ( 1 - l1 )/3
            s1 = 1 - 2*r1
            g(0,gms+1) = s1
            call smpcpy( 1, mxg, g(0,gms+1), r1 )
            pts(gms+1) = 3
              w(gms+1,iw) = one/6
            r2 = ( 1 - l2 )/3
            s2 = 1 - 2*r2
            g(0,gms+2) = s2
            call smpcpy( 1, mxg, g(0,gms+2), r2 )
            pts(gms+2) = 3
              w(gms+2,iw) = one/6
         else
c
c  Degree 3 rule using Stroud points.
c
            r2 = ( dr + 4 + sqrt(ffteen) )/( dr*dr + 8*dr + 1 )
            s2 = 1 - dr*r2
            l2 = s2 - r2
            g(0,gms+2) = s2
            call smpcpy( 1, mxg, g(0,gms+2), r2 )
            pts(gms+2) = int ( dr + 1 )
            w(gms+2,iw) = ( 2/(dr+3) - l1 )/( dr2*(l2-l1)*l2**2 )
            w(gms+1,iw) = ( 2/(dr+3) - l2 )/( dr2*(l1-l2)*l1**2 )
         end if
         iw = iw - 1
c
c        Grundmann-Moller degree 5 rule.
c
         g(0,3) = 5/( dr + 5 )
         call smpcpy( 1, mxg, g(0,3), 1/( dr + 5 ) )
         pts(3) = int ( dr + 1 )
         g(0,4) = 3/( dr + 5 )
         g(1,4) = 3/( dr + 5 )
         call smpcpy( 2, mxg, g(0,4), 1/( dr + 5 ) )
         pts(4) = int ( ( ( dr + 1 )*dr )/2 )
         w(2,iw) = -( dr + 3 )**5/( 16*dr4 )
         w(3,iw) =  ( dr + 5 )**5/( 16*dr4*( dr + 5 ) )
         w(4,iw) =  ( dr + 5 )**5/( 16*dr4*( dr + 5 ) )
      end if
      if ( key .gt. 2 )  then
         iw = iw - 1
c
c  Compute weights, generators and PTS for degree 5 and 7 rules.
c
c
c  Stroud degree 5 rule.
c
         u1 = ( dr + 7 + 2*sqrt(ffteen) )/( dr*dr + 14*dr - 11 )
         v1 = ( 1 - ( dr - 1 )*u1 )/2
         d1 = v1 - u1
         g(0,gms+3) = v1
         g(1,gms+3) = v1
         call smpcpy( 2, mxg, g(0,gms+3), u1 )
         pts(gms+3) = int ( ( ( dr + 1 )*dr )/2 )
         u2 = ( dr + 7 - 2*sqrt(ffteen) )/( dr*dr + 14*dr - 11 )
         v2 = ( 1 - ( dr - 1 )*u2 )/2
         d2 = v2 - u2
         g(0,gms+4) = v2
         g(1,gms+4) = v2
         call smpcpy( 2, mxg, g(0,gms+4), u2 )
         pts(gms+4) = int ( ( ( dr + 1 )*dr )/2 )
         if ( nd .eq. 2 ) then
            w(gms+3,iw) = ( 155 - sqrt(ffteen) )/1200
            w(gms+4,iw) = ( 155 + sqrt(ffteen) )/1200
            w(1,    iw) = 1 - 3*( w(gms+3,iw) + w(gms+4,iw) )
         else if ( nd .eq. 3 ) then
            w(gms+1,iw) = ( 2665 + 14*sqrt(ffteen) )/37800
            w(gms+2,iw) = ( 2665 - 14*sqrt(ffteen) )/37800
            w(gms+3,iw) = 2*ffteen/567
            pts(gms+4) = 0
         else
            w(gms+1,iw) = ( 2*(27-dr)/(dr+5)-l2*(13-dr) )
     &                       /( l1**4*(l1-l2)*dr4 )
            w(gms+2,iw) = ( 2*(27-dr)/(dr+5)-l1*(13-dr) )
     &                       /( l2**4*(l2-l1)*dr4 )
            w(gms+3,iw)=( 2/( dr + 5 ) - d2 )/( dr4*( d1 - d2 )*d1**4 )
            w(gms+4,iw)=( 2/( dr + 5 ) - d1 )/( dr4*( d2 - d1 )*d2**4 )
         end if
         iw = iw - 1
c
c  Grundmann-Moller degree 7 rule.
c
         g(0,5) = 7/( dr + 7 )
         call smpcpy( 1, mxg, g(0,5), 1/( dr + 7 ) )
         pts(5) = int ( dr + 1 )
         g(0,6) = 5/( dr + 7 )
         g(1,6) = 3/( dr + 7 )
         call smpcpy( 2, mxg, g(0,6), 1/( dr + 7 ) )
         pts(6) = int ( ( dr + 1 )*dr )
         g(0,7) = 3/( dr + 7 )
         g(1,7) = 3/( dr + 7 )
         g(2,7) = 3/( dr + 7 )
         call smpcpy( 3, mxg, g(0,7), 1/( dr + 7 ) )
         pts(7) = int ( ( ( dr + 1 )*dr*( dr - 1 ) )/6 )
         w(2,iw) =  ( dr + 3 )**7/( 2*64*dr4*( dr + 5 ) )
         w(3,iw) = -( dr + 5 )**7/(   64*dr6 )
         w(4,iw) = -( dr + 5 )**7/(   64*dr6 )
         w(5,iw) =  ( dr + 7 )**7/(   64*dr6*( dr + 7 ) )
         w(6,iw) =  ( dr + 7 )**7/(   64*dr6*( dr + 7 ) )
         w(7,iw) =  ( dr + 7 )**7/(   64*dr6*( dr + 7 ) )
      end if
      if ( key .eq. 4 )  then
         iw = iw - 1
c
c  Compute weights, generators and PTS for degree 7, 9 rules.
c
c  Mysovskikh degree 7 rule.
c
         sg = 1/( 23328*dr6 )
         u5 = -6**3*sg*( 52212 - dr*( 6353 + dr*( 1934 - dr*27 ) ) )
         u6 =  6**4*sg*(  7884 - dr*( 1541 - dr*9 ) )
         u7 = -6**5*sg*(  8292 - dr*( 1139 - dr*3 ) )/( dr + 7 )
         p0 = -144*( 142528 + dr*( 23073 - dr*115 ) )
         p1 = -12*( 6690556 + dr*( 2641189 + dr*( 245378 - dr*1495 ) ) )
         p2 = -16*(6503401 + dr*(4020794+dr*(787281+dr*(47323-dr*385))))
         p3 = -( 6386660 + dr*(4411997+dr*(951821+dr*(61659-dr*665))) )
     &        *( dr + 7 )
         a = p2/( 3*p3 )
         p = a*( p1/p2 - a )
         q = a*( 2*a*a - p1/p3 ) + p0/p3
         r = sqrt( -p**3 )
         th = acos( -q/( 2*r ) )/3
         r = 2*r**( one/3 )
         tp = 2*acos(-one)/3
         a1 = -a + r*cos( th )
         a2 = -a + r*cos( th + tp + tp )
         a3 = -a + r*cos( th + tp )
         g(0,gms+5) = ( 1 - dr*a1 )/( dr + 1 )
         call smpcpy( 1, mxg, g(0,gms+5), ( 1 + a1 )/( dr + 1 ) )
         pts(gms+5) = int ( dr + 1 )
         g(0,gms+6) = ( 1 - dr*a2 )/( dr + 1 )
         call smpcpy( 1, mxg, g(0,gms+6), ( 1 + a2 )/( dr + 1 ) )
         pts(gms+6) = int ( dr + 1 )
         g(0,gms+7) = ( 1 - dr*a3 )/( dr + 1 )
         call smpcpy( 1, mxg, g(0,gms+7), ( 1 + a3 )/( dr + 1 ) )
         pts(gms+7) = int ( dr + 1 )
         w(gms+5,iw) = ( u7-(a2+a3)*u6+a2*a3*u5 )
     &                  /( a1**2-(a2+a3)*a1+a2*a3 )/a1**5
         w(gms+6,iw) = ( u7-(a1+a3)*u6+a1*a3*u5 )
     &                  /( a2**2-(a1+a3)*a2+a1*a3 )/a2**5
         w(gms+7,iw) = ( u7-(a2+a1)*u6+a2*a1*u5 )
     &                  /( a3**2-(a2+a1)*a3+a2*a1 )/a3**5
         g(0,gms+8) = 4/( dr + 7 )
         g(1,gms+8) = 4/( dr + 7 )
         call smpcpy( 2, mxg, g(0,gms+8), 1/( dr + 7 ) )
         pts(gms+8) = int ( ( ( dr + 1 )*dr )/2 )
         w(gms+8,iw) = 10*(dr+7)**6/( 729*dr6 )
         g(0,gms+9) = 11/( dr + 7 )/2
         g(1,gms+9) =  5/( dr + 7 )/2
         call smpcpy( 2, mxg, g(0,gms+9), 1/( dr + 7 ) )
         pts(gms+9) = int ( ( dr + 1 )*dr ) 
         w(gms+9,iw) = 64*(dr+7)**6/( 6561*dr6 )
         w(    4,iw) = w(4,iw+1)
         w(    7,iw) = w(7,iw+1)
         iw = iw - 1
c
c  Grundmann-Moller degree 9 rule.
c
         g(0,8) = 9/( dr + 9 )
         call smpcpy( 1, mxg, g(0, 8), 1/( dr + 9 ) )
         pts(8) = int ( dr + 1 )
         g(0,9) = 7/( dr + 9 )
         g(1,9) = 3/( dr + 9 )
         call smpcpy( 2, mxg, g(0, 9), 1/( dr + 9 ) )
         pts(9) = int ( ( dr + 1 )*dr )
         g(0,10) = 5/( dr + 9 )
         g(1,10) = 5/( dr + 9 )
         call smpcpy( 2, mxg, g(0,10), 1/( dr + 9 ) )
         pts(10) = int ( ( ( dr + 1 )*dr )/2 )
         g(0,11) = 5/( dr + 9 )
         g(1,11) = 3/( dr + 9 )
         g(2,11) = 3/( dr + 9 )
         call smpcpy( 3, mxg, g(0,11), 1/( dr + 9 ) )
         pts(11) = ( int ( ( dr + 1 )*dr*( dr - 1 ) )/2 )
         w(2 ,iw) = -( dr + 3 )**9/( 6*256*dr6 )
         w(3 ,iw) =  ( dr + 5 )**9/( 2*256*dr6*( dr + 7 ) )
         w(4 ,iw) =  ( dr + 5 )**9/( 2*256*dr6*( dr + 7 ) )
         w(5 ,iw) = -( dr + 7 )**9/(   256*dr8 )
         w(6 ,iw) = -( dr + 7 )**9/(   256*dr8 )
         w(7 ,iw) = -( dr + 7 )**9/(   256*dr8 )
         w(8 ,iw) =  ( dr + 9 )**9/(   256*dr8*( dr + 9 ) )
         w(9 ,iw) =  ( dr + 9 )**9/(   256*dr8*( dr + 9 ) )
         w(10,iw) =  ( dr + 9 )**9/(   256*dr8*( dr + 9 ) )
         w(11,iw) =  ( dr + 9 )**9/(   256*dr8*( dr + 9 ) )
         if ( nd .gt. 2 ) then
            g(0,12) = 3/( dr + 9 )
            g(1,12) = 3/( dr + 9 )
            g(2,12) = 3/( dr + 9 )
            g(3,12) = 3/( dr + 9 )
            call smpcpy( 4, mxg, g(0,12), 1/( dr + 9 ) )
            pts(12) = 
     &        int ( ( ( dr + 1 )*dr*( dr - 1 )*( dr - 2 ) )/24 )
            w(12,iw) = w(8,iw)
         end if
      end if
c
c  Compute constant weight values.
c
      do j = 1, rls
         w(1,j) = 1
         do i = 2, wts
            w(1,j) = w(1,j) - pts(i)*w(i,j)
         end do
      end do
c
c  Compute final weight values; null rule weights are computed as
c  differences between weights from highest degree and lower degree rules.
c
      do j = 2, rls
         do i = 1, wts
            w(i,j) = w(i,j) - w(i,1)
         end do
      end do

      return
      end
      subroutine smpcpy ( start, end, param, value )

c*********************************************************************72
c
cc SMPCPY sets a vector to a constant.
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
      double precision value, param(0:*)
      integer start, end, i
      do i = start, end
         param(i) = value
      end do

      return
      end
      subroutine smpsms ( n, vertex, nf, f, g, symsms, x, funvls )

c*********************************************************************72
c
cc SMPSMS computes a symmetric sum over a simplex.
c
c***BEGIN PROLOGUE SMPSMS
c***KEYWORDS fully symmetric sum
c***PURPOSE  To compute fully symmetric basic rule sums
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***DESCRIPTION SMPSMS computes a fully symmetric sum for a vector
c            of integrand values over a simplex. The sum is taken over
c            all permutations of the generators for the sum.
c
c   ON ENTRY
c
c   N       Integer, number of variables.
c   VERTEX  Real array of dimension (N,0:N)
c           The vertices of the simplex, one vertex per column.
c   NF      Integer, number of components for the vector integrand.
c   F       Subroutine for computing components of the integrand at X.
c            It must have parameters ( N, X, NF, FUNVLS );
c            Input parameters:
c              N      Integer dimension of integral.
c              X      Real array of length N, the evaluation point.
c              NF     Integer number of components of the integrand.
c            Output parameter:
c             FUNVLS  Real array of length NF, the integrand values at X.
c   G       Real Array of dimension (0:N).
c           The generators for the fully symmetric sum.
c
c   ON RETURN
c
c   SYMSMS  Real array of length NF, the values for the fully symmetric
c            sums for each component of the integrand.
c
c***ROUTINES CALLED: Integrand
c
c***END PROLOGUE SMPSMS
c
c   Global variables.
c
      integer n, nf
      double precision vertex(n,0:n),g(0:n), symsms(nf),funvls(nf), x(n)
c
c   Local variables.
c
      integer ix, lx, i, j, k, l
      double precision gl, gi
c
c***FIRST PROCESSING STATEMENT SymSum
c
      do i = 1, nf
         symsms(i) = 0
      end do
c
c  Sort generators if necessary
c
      k = 0
      do i = 1, n
         if ( g(i) .gt. g(i-1) ) k = 1
      end do
      if ( k .gt. 0 ) then
         do i = 1, n
            k = i - 1
            do j = i, n
               if ( g(j) .gt. g(k) ) k = j
            end do
            if ( k .ge. i ) then
               gi = g(i-1)
               g(i-1) = g(k)
               g(k) = gi
            end if
         end do
      end if
c
c  Compute integrand value for permutations of G
c
 10   continue

      do i = 1, n
         x(i) = vertex(i,0)*g(0)
         do j = 1, n
            x(i) = x(i) + vertex(i,j)*g(j)
         end do
      end do
      call f( n, x, nf, funvls )
      do j = 1, nf
         symsms(j) = symsms(j) + funvls(j)
      end do
c
c  Find next distinct permuation of G and loop back for value.
c  Permutations are generated in reverse lexicographic order.
c
      do i = 1, n
         if ( g(i-1) .gt. g(i) ) then
            gi = g(i)
            ix = i - 1
            do l = 0, i/2-1
               gl = g(l)
               g(l) = g(i-l-1)
               g(i-l-1) = gl
               if (  gl .le. gi ) ix = ix - 1
               if ( g(l) .gt. gi ) lx = l
            end do
            if ( g(ix) .le. gi ) ix = lx
            g(i) = g(ix)
            g(ix) = gi
            go to 10
         end if
      end do

      return
      end
      subroutine smpstr ( pointr, sbrgns, pontrs, rgners )

c*********************************************************************72
c
cc SMPSTR maintains a heap to keep track of subregions.
c
c***BEGIN PROLOGUE SMPSTR
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***PURPOSE SMPSTR maintains a heap for subregions.
c***DESCRIPTION SMPSTR maintains a heap for subregions.
c            The subregions are ordered according to the size of the
c            greatest error estimates of each subregion (RGNERS).
c
c   PARAMETERS
c
c     POINTR Integer.
c            The index for the subregion to be inserted in the heap.
c     SBRGNS Integer.
c            Number of subregions in the heap.
c     PONTRS Real array of dimension SBRGNS.
c            Used to store the indices for the greatest estimated errors
c            for each subregion.
c     RGNERS Real array of dimension SBRGNS.
c            Used to store the greatest estimated errors for each
c            subregion.
c
c***ROUTINES CALLED NONE
c***END PROLOGUE SMPSTR
c
c   Global variables.
c
      integer pointr, sbrgns
      double precision pontrs(*), rgners(*)
c
c   Local variables.
c
c   RGNERR Intermediate storage for the greatest error of a subregion.
c   SUBRGN Position of child/parent subregion in the heap.
c   SUBTMP Position of parent/child subregion in the heap.
c
      integer subrgn, subtmp, pt, ptp
      double precision rgnerr
c
c***FIRST PROCESSING STATEMENT SMPSTR
c
      rgnerr = rgners(pointr)
      if ( pointr .eq. pontrs(1) ) then
c
c  Move the new subregion inserted at the top of the heap
c  to its correct position in the heap.
c
         subrgn = 1

 10      continue

         subtmp = 2*subrgn

         if ( subtmp .le. sbrgns ) then
            if ( subtmp .ne. sbrgns ) then
c
c  Find maximum of left and right child.
c
               pt = int ( pontrs(subtmp) )
               ptp = int ( pontrs(subtmp+1) )
               if ( rgners(pt) .lt. rgners(ptp) ) subtmp = subtmp + 1
            end if
c
c  Compare maximum child with parent.
c  If parent is maximum, then done.
c
            pt = int ( pontrs(subtmp) )
            if ( rgnerr .lt. rgners(pt) ) then
c
c  Move the pointer at position subtmp up the heap.
c
               pontrs(subrgn) = pt
               subrgn = subtmp
               go to 10
            end if
         end if
      else
c
c  Insert new subregion in the heap.
c
         subrgn = sbrgns

 20      continue

         subtmp = subrgn/2
         if ( subtmp .ge. 1 ) then
c
c  Compare child with parent. If parent is maximum, then done.
c
            pt = int ( pontrs(subtmp) )
            if ( rgnerr .gt. rgners(pt) ) then
c
c  Move the pointer at position subtmp down the heap.
c
               pontrs(subrgn) = pt
               subrgn = subtmp
               go to 20
            end if
         end if
      end if
      pontrs(subrgn) = pointr

      return
      end
      subroutine smpdfs ( nd, nf, funsub, top, sbrgns, vertcs, volums,
     &  x, h, center, work, frthdf, newsbs )

c*********************************************************************72
c
cc SMPDFS determines how to subdivide the subregion.
c
c***BEGIN PROLOGUE SMPDFS
c***PURPOSE  To compute new subregions
c
c  Modified:
c
c    07 July 2007
c
c  Author:
c
c    Alan Genz
c    Department of Mathematics
c    Washington State University
c    Pullman, WA 99164-3113, USA
c
c***DESCRIPTION SMPDFS computes fourth differences along each edge
c            direction. It uses these differences to determine a
c            subdivision of the orginal subregion into either three or
c            four new subregions.
c
c   ON ENTRY
c
c   ND   Integer, number of variables.
c   NF   Integer, number of components for the vector integrand.
c   FUNSUB Externally declared subroutine.
c          For computing the components of the integrand at a point X.
c          It must have parameters (ND, X, NF, FUNVLS).
c           Input Parameters:
c            X  Real array of dimension ND, the evaluation point.
c            ND Integer, number of variables for the integrand.
c            NF Integer, number of components for the vector integrand.
c           Output Parameters:
c            FUNVLS Real array of dimension NF.
c                   The components of the integrand at the point X.
c   TOP    Integer, location in VERTCS array for original subregion.
c   SBRGNS Integer, number of subregions in VERTCS BEFORE subdivision.
c   VERTCS Real array of dimension (ND,0:ND,*), vertices of orginal
c          subregion must be in VERTCS(1:ND,0:ND,TOP).
c   VOLUMS Real array of dimension (*) of volumes for subregions.
c   X      Real work array of dimension ND.
c   H      Real work array of dimension ND.
c   CENTER Real work array of dimension (0:ND).
c   WORK   Real work array of dimension 5*NF.
c   FRTHDF Real work array of dimension (0:ND-1,ND).
c
c   ON RETURN
c
c   NEWSBS Integer, number of new subregions (3 or 4).
c   FUNCLS Integer, number of FUNSUB calls used by SMPDFS.
c   VERTCS Real array of dimension (ND,0:ND,*).
c          The vertices of the of new subegions will be at locations
c          TOP, SBRGNS+1, ..., SBRGNS+NEWSBS-1.
c   VOLUMS Real Array of dimension (*).
c          VOLUMS has been updated for new subregions.
c
c***ROUTINES CALLED: FUNSUB
c
c***END PROLOGUE SMPDFS
c
      external funsub
      integer nd, nf, top, sbrgns, newsbs
      double precision vertcs(nd,0:nd,*), volums(*), work(nf,*)
      double precision x(nd), h(nd), center(nd), frthdf(0:nd-1,nd)
      double precision differ, difmax, difmid, difnxt, ewidth, edgmax
      double precision cuttf, cuttb, difil, diflj, dfsmax, vti, vtj, vtl
      parameter ( cuttf = 2, cuttb = 8 )
      integer i, j, k, l, ie, je,  is, js, ls, it, jt, lt
c
c***FIRST PROCESSING STATEMENT SMPDFS
c
c
c       Compute the differences.
c
      is = 0
      js = 1
      difmax = 0
      edgmax = 0
      do k = 1, nd
         center(k) = vertcs(k,0,top)
         do l = 1, nd
            center(k) = center(k) + vertcs(k,l,top)
         end do
         center(k) = center(k)/( nd + 1 )
      end do
      call funsub(nd, center, nf, work(1,3))
      do i = 0, nd-1
         do j = i+1, nd
            ewidth = 0
            do k = 1, nd
               h(k) = 2*( vertcs(k,i,top)-vertcs(k,j,top) )/( 5*(nd+1) )
               ewidth = ewidth + abs( h(k) )
               x(k) = center(k) - 3*h(k)
            end do
            do l = 1, 5
               do k = 1, nd
                  x(k) = x(k) + h(k)
               end do
               if ( l. ne. 3 ) call funsub(nd, x, nf, work(1,l))
            end do
            if ( ewidth .ge. edgmax ) then
               ie = i
               je = j
               edgmax = ewidth
            end if
            differ = 0
            difmid = 0
            do k = 1, nf
               difmid = difmid + abs( work(k,3) )
               differ = differ + abs( work(k,1) + work(k,5)+ 6*work(k,3)
     &                                - 4*( work(k,2) + work(k,4) ) )
            end do
            if ( difmid + differ/8 .eq. difmid ) differ = 0
            differ = differ*ewidth
            frthdf(i,j) = differ
            if ( differ .ge. difmax ) then
               it = is
               jt = js
               difnxt = difmax
               is = i
               js = j
               difmax = differ
            else if ( differ .ge. difnxt ) then
               it = i
               jt = j
               difnxt = differ
            end if
         end do
      end do
c
c  Determine whether to compute three or four new subregions.
c
      if ( difnxt .gt. difmax/cuttf ) then
         newsbs = 4
      else
         newsbs = 3
         if ( difmax .eq. 0 ) then
            is = ie
            js = je
         else
            dfsmax = 0
            do l = 0, nd
               if ( l .ne. is .and. l .ne. js ) then
                  it = min( l, is, js )
                  jt = max( l, is, js )
                  lt = is + js + l - it - jt
                  differ =  frthdf(it,lt) + frthdf(lt,jt)
                  if ( differ .ge. dfsmax ) then
                     dfsmax = differ
                     ls = l
                  end if
               end if
            end do
            difil = frthdf( min(is,ls), max(is,ls) )
            diflj = frthdf( min(js,ls), max(js,ls) )
            difnxt = difil + diflj - min( difil,diflj )
            if ( difmax/cuttb .lt. difnxt .and. difil .gt. diflj ) then
               it = is
               is = js
               js = it
            end if
         end if
      end if
c
c  Copy vertices and volume for TOP to new subregions
c
      volums(top) = volums(top)/newsbs
      do l = sbrgns + 1, sbrgns + newsbs - 1
         volums(l) = volums(top)
         do j = 0, nd
            do k = 1, nd
               vertcs(k,j,l) = vertcs(k,j,top)
            end do
         end do
      end do
      do k = 1, nd
         vti = vertcs(k,is,top)
         vtj = vertcs(k,js,top)
         if ( newsbs .eq. 4 ) then
c
c  Compute four new subregions.
c
            vertcs(k,js,top)      = ( vti + vtj )/2
            vertcs(k,is,sbrgns+1) = vti
            vertcs(k,js,sbrgns+1) = ( vti + vtj )/2
            vertcs(k,is,sbrgns+2) = ( vti + vtj )/2
            vertcs(k,js,sbrgns+2) = vtj
            vertcs(k,is,sbrgns+3) = ( vti + vtj )/2
            vertcs(k,js,sbrgns+3) = vtj
            vti = vertcs(k,it,top)
            vtj = vertcs(k,jt,top)
            vertcs(k,jt,top)      = ( vti + vtj )/2
            vertcs(k,it,sbrgns+1) = ( vti + vtj )/2
            vertcs(k,jt,sbrgns+1) = vtj
            vti = vertcs(k,it,sbrgns+2)
            vtj = vertcs(k,jt,sbrgns+2)
            vertcs(k,jt,sbrgns+2) = ( vti + vtj )/2
            vertcs(k,it,sbrgns+3) = ( vti + vtj )/2
            vertcs(k,jt,sbrgns+3) = vtj
         else
c
c  Compute three new subregions.
c
            vertcs(k,js,top)      = ( 2*vti + vtj )/3
            vertcs(k,is,sbrgns+1) = ( 2*vti + vtj )/3
            if ( difmax/cuttf .lt. difnxt ) then
               vertcs(k,js,sbrgns+1) = vtj
               vertcs(k,is,sbrgns+2) = ( 2*vti + vtj )/3
               vertcs(k,js,sbrgns+2) = vtj
               vtj = vertcs(k,js,sbrgns+1)
               vtl = vertcs(k,ls,sbrgns+1)
               vertcs(k,ls,sbrgns+1) = ( vtj + vtl )/2
               vertcs(k,js,sbrgns+2) = ( vtj + vtl )/2
               vertcs(k,ls,sbrgns+2) = vtl
            else
               vertcs(k,js,sbrgns+1) = ( vti + 2*vtj )/3
               vertcs(k,is,sbrgns+2) = ( vti + 2*vtj )/3
               vertcs(k,js,sbrgns+2) = vtj
            end if
         end if
      end do

      return
      end
      subroutine timestamp ( )

c*********************************************************************72
c
cc TIMESTAMP prints out the current YMDHMS date as a timestamp.
c
c  Discussion:
c
c    This Fortran77 version is made available for cases where the
c    Fortran90 version cannot be used.
c
c  Modified:
c
c    12 January 2007
c
c  Author:
c
c    John Burkardt
c
c  Parameters:
c
c    None
c
      implicit none

      character * ( 8 ) ampm
      integer d
      character * ( 8 ) date
      integer h
      integer m
      integer mm
      character * ( 9 ) month(12)
      integer n
      integer s
      character * ( 10 ) time
      integer y

      save month

      data month /
     &  'January  ', 'February ', 'March    ', 'April    ',
     &  'May      ', 'June     ', 'July     ', 'August   ',
     &  'September', 'October  ', 'November ', 'December ' /

      call date_and_time ( date, time )

      read ( date, '(i4,i2,i2)' ) y, m, d
      read ( time, '(i2,i2,i2,1x,i3)' ) h, n, s, mm

      if ( h .lt. 12 ) then
        ampm = 'AM'
      else if ( h .eq. 12 ) then
        if ( n .eq. 0 .and. s .eq. 0 ) then
          ampm = 'Noon'
        else
          ampm = 'PM'
        end if
      else
        h = h - 12
        if ( h .lt. 12 ) then
          ampm = 'PM'
        else if ( h .eq. 12 ) then
          if ( n .eq. 0 .and. s .eq. 0 ) then
            ampm = 'Midnight'
          else
            ampm = 'AM'
          end if
        end if
      end if

      write ( *,
     &  '(i2,1x,a,1x,i4,2x,i2,a1,i2.2,a1,i2.2,a1,i3.3,1x,a)' )
     &  d, month(m), y, h, ':', n, ':', s, '.', mm, ampm

      return
      end
