GAUSS_QUADRATURE.f90 Source File


Files dependent on this one

sourcefile~~gauss_quadrature.f90~~AfferentGraph sourcefile~gauss_quadrature.f90 GAUSS_QUADRATURE.f90 sourcefile~sf_integrate.f90 SF_INTEGRATE.f90 sourcefile~sf_integrate.f90->sourcefile~gauss_quadrature.f90 sourcefile~scifor.f90 SCIFOR.f90 sourcefile~scifor.f90->sourcefile~sf_integrate.f90 sourcefile~sf_fft.f90 SF_FFT.f90 sourcefile~scifor.f90->sourcefile~sf_fft.f90 sourcefile~sf_special.f90 SF_SPECIAL.f90 sourcefile~scifor.f90->sourcefile~sf_special.f90 sourcefile~sf_stat.f90 SF_STAT.f90 sourcefile~scifor.f90->sourcefile~sf_stat.f90 sourcefile~sf_fft.f90->sourcefile~sf_integrate.f90 sourcefile~sf_special.f90->sourcefile~sf_integrate.f90 sourcefile~sf_stat.f90->sourcefile~sf_integrate.f90

Source Code

!ENTIRELY BASED ON THE WORK OF 
! Jacob Williams https://github.com/jacobwilliams/quadrature-fortran
!SEE There for a more object-oriented approach and further information.
!LICENSE CAN BE FOUND HERE:
!https://github.com/jacobwilliams/quadrature-fortran/blob/master/LICENSE
!
!LIBRARY EXTENDED TO DEAL WITH:
! 1D, 2D sampled functions (dble arrays)
! vectorial functions of vectorial variables F:R^n-->R^m
! this required to slightly change *dgauss_generic routine and gX=6,8,10,12,14
! to handle multiple functions simultaneously.
MODULE GAUSS_QUADRATURE
  implicit none
  private
  real(8),parameter :: zero      = 0.0d0
  real(8),parameter :: one_half  = 0.5d0
  real(8),parameter :: one       = 1.0d0
  real(8),parameter :: two       = 2.0d0
  real(8),parameter :: three     = 3.0d0
  real(8),parameter :: four      = 4.0d0

  type :: integration_type
     real(8)                                         :: val
     real(8)                                         :: tol
     real(8)                                         :: a
     real(8)                                         :: b
     integer                                         :: dim=1
     !
     procedure(gauss_func_method),pointer            :: g  => null()
     procedure(f_x),pointer,nopass                   :: fx => null()
     procedure(f_xvec),pointer,nopass                :: fxvec => null()
     !
     type(integration_type),dimension(:),allocatable :: ivec
  end type integration_type


  abstract interface
     !Fvec(x): R-->R^m integrand
     function f_x(x,m) result(f)
       real(8) :: x
       integer :: m
       real(8) :: f(m)
     end function f_x

     !F(xvec): R^n-->R^m integrand  
     function f_xvec(x,m) result(f)
       real(8) :: x(:)          !n
       integer :: m
       real(8) :: f(m)
     end function f_xvec

     !Gaussian quadrature method to be used in integration
     function gauss_func_method(self, x, h, m) result(f)
       import :: integration_type
       class(integration_type),intent(inout) :: self
       real(8), intent(in)                   :: x
       real(8), intent(in)                   :: h
       integer                               :: m
       real(8)                               :: f(m)
     end function gauss_func_method
  end interface

  interface gauss_quad
     module procedure :: integrate_1d_func_main
     module procedure :: integrate_nd_func_main
     module procedure :: integrate_1d_func_1
     module procedure :: integrate_nd_func_1
     module procedure :: integrate_1d_sample
     module procedure :: integrate_2d_sample
  end interface gauss_quad

  interface integrate
     module procedure :: integrate_1d_func_main
     module procedure :: integrate_nd_func_main
     module procedure :: integrate_1d_func_1
     module procedure :: integrate_nd_func_1
     module procedure :: integrate_1d_sample
     module procedure :: integrate_2d_sample
  end interface integrate

  public :: gauss_quad
  public :: integrate



  !FUNCTION INTERPOLATION TYPE: used to integrate sampled functions
  type finter1d_type
     real(8),allocatable,dimension(:)   :: X
     real(8),allocatable,dimension(:)   :: F
     real(8),allocatable,dimension(:)   :: G
     integer                            :: N=0
     integer                            :: Imin=0,Imax=0
     logical                            :: status=.false.
  end type finter1d_type

  type finter2d_type
     real(8),allocatable,dimension(:)   :: X
     real(8),allocatable,dimension(:)   :: y
     real(8),allocatable,dimension(:,:) :: F 
     integer                            :: N=0
     integer                            :: Imin=0,Imax=0
     integer                            :: Jmin=0,Jmax=0
     logical                            :: status=.false.
  end type finter2d_type



contains



  subroutine init_type(self,xl,xu,tol,method)
    type(integration_type)    :: self     !! for the 1d integration
    real(8),intent(in)        :: xl       !! x integration lower bound
    real(8),intent(in)        :: xu       !! x integration upper bound
    real(8),intent(in)        :: tol      !! error tolerance for dx integration
    integer,intent(in)        :: method   !! quadrature method to use for x
    !
    select case (method)
    case(6);  self%g => g6
    case(8);  self%g => g8
    case(10); self%g => g10
    case(12); self%g => g12
    case(14); self%g => g14
    case default
       error stop 'invalid quadrature method in initialize integration: method=6,8,10,12,14'
    end select
    !
    self%tol    =  tol             !tolerance
    self%a      =  xl              !lower bound
    self%b      =  xu              !upper bound
  end subroutine init_type




  !##################################################################
  !##################################################################
  !               FUNCTIONS: nD R^n --> R^m
  !              m=1 is available as a simplified interface
  !##################################################################
  !##################################################################

  subroutine integrate_1d_func_main(m,fx,xl,xu,ans,tol,method,ierr,err)
    integer                            :: m
    procedure(f_x)                     :: fx       !! 1d function: f(x)
    real(8),intent(in)                 :: xl       !! x integration lower bound
    real(8),intent(in)                 :: xu       !! x integration upper bound
    real(8),intent(inout),dimension(m) :: ans
    !
    real(8),intent(in),optional        :: tol     !! error tolerance for dx integration
    integer,intent(in),optional        :: method  !! quadrature method to use for x
    integer,intent(out),optional       :: ierr
    real(8),intent(out),optional       :: err
    !
    type(integration_type)             :: self     !! for the 1d integration
    !
    real(8)                            :: tol_
    integer                            :: method_
    integer                            :: ierr_
    real(8)                            :: err_
    !
    tol_   = 1d-9 ; if(present(tol))tol_ = tol
    method_= 10   ; if(present(method))method_=method
    !
    self%fx => fx
    call init_type(self,xl,xu,tol_,method)
    call dgauss_generic(self,ans, ierr_,err_,m)
    select case(ierr_)
    case(-1)
       stop "Lower Bound ~ Upper Bound => Ans set to zero"
    case(2)
       stop "Dgauss_Generic ABNORMAL EXIT CODE: ANS does not meet requested tolerance"
    case default
       continue
    end select
    if(present(ierr))ierr=ierr_
    if(present(err))err=err_
  end subroutine integrate_1d_func_main


  subroutine integrate_1d_func_1(func,xl,xu,ans,tol,method,ierr,err)
    interface
       function func(x)
         real(8) :: x
         real(8) :: func
       end function func
    end interface
    real(8),intent(in)                 :: xl       !! x integration lower bound
    real(8),intent(in)                 :: xu       !! x integration upper bound
    real(8),intent(inout)              :: ans
    !
    real(8),intent(in),optional        :: tol     !! error tolerance for dx integration
    integer,intent(in),optional        :: method  !! quadrature method to use for x
    integer,intent(out),optional       :: ierr
    real(8),intent(out),optional       :: err
    !
    real(8)                            :: tol_,ans_(1)
    integer                            :: method_
    integer                            :: ierr_
    real(8)                            :: err_
    tol_   = 1d-9 ; if(present(tol))tol_ = tol
    method_= 10   ; if(present(method))method_=method
    call integrate_1d_func_main(1,fx,xl,xu,ans_,tol_,method_,ierr_,err_)
    ans = ans_(1)
    if(present(ierr))ierr=ierr_
    if(present(err))err=err_
  contains
    function fx(x,m)
      real(8) :: x
      integer :: m
      real(8) :: fx(m)
      fx(1) = func(x)
    end function fx
  end subroutine integrate_1d_func_1


  subroutine integrate_nd_func_main(m,fxvec,xl,xu,ans,methods,method,tols,tol,ierr,err)
    integer                                         :: m
    procedure(f_xvec)                               :: fxvec    !! 2d function: f(x,y)
    real(8),dimension(:),intent(in)                 :: xl       !! integration lower bounds
    real(8),dimension(size(xl)),intent(in)          :: xu       !! integration upper bounds
    real(8),intent(inout),dimension(m)              :: ans
    !
    integer,dimension(size(xl)),intent(in),optional :: methods  !! quadrature methods nD-->nD to use
    integer,intent(in),optional                     :: method   !! quadrature method   1-->nD to use
    real(8),dimension(size(xl)),intent(in),optional :: tols     !! error tolerances nD-->nD for dx integration
    real(8),intent(in),optional                     :: tol     !! error tolerance   1-->nD for dx integration
    integer,intent(out),optional                    :: ierr
    real(8),intent(out),optional                    :: err
    !
    real(8),dimension(size(xl))                     :: tol_
    integer,dimension(size(xl))                     :: method_
    integer                                         :: ierr_
    real(8)                                         :: err_
    !
    integer                                         :: i
    type(integration_type)                          :: self     !! for the 1d integration
    !
    tol_   = 1d-9 ; if(present(tol))tol_ = tol; if(present(tols))tol_ = tols
    method_= 10   ; if(present(method))method_=method; if(present(methods))method_=methods
    !
    self%fxvec => fxvec  !the user-defined f(x,y) function to integrate
    self%dim   = size(xl)
    allocate(self%ivec(self%dim))
    select case (self%dim)
    case default
       stop "INTEGRATE_ND ERROR: calling multi-dimensional quadrature for dimension N>6. Ask developer or DIY."
    case(1)
       stop "INTEGRATE_ND ERROR: calling multi-dimensional quadrature for 1d problem. Use scalar version."
    case (2)
       self%ivec(1)%fx => f_of_x1
       self%ivec(2)%fx => f_of_x2
    case (3)
       self%ivec(1)%fx => f_of_x1
       self%ivec(2)%fx => f_of_x2
       self%ivec(3)%fx => f_of_x3
    case (4)
       self%ivec(1)%fx => f_of_x1
       self%ivec(2)%fx => f_of_x2
       self%ivec(3)%fx => f_of_x3
       self%ivec(4)%fx => f_of_x4
    case (5)
       self%ivec(1)%fx => f_of_x1
       self%ivec(2)%fx => f_of_x2
       self%ivec(3)%fx => f_of_x3
       self%ivec(4)%fx => f_of_x4
       self%ivec(5)%fx => f_of_x5
    case (6)
       self%ivec(1)%fx => f_of_x1
       self%ivec(2)%fx => f_of_x2
       self%ivec(3)%fx => f_of_x3
       self%ivec(4)%fx => f_of_x4
       self%ivec(5)%fx => f_of_x5
       self%ivec(6)%fx => f_of_x6
    end select
    !
    do i=1,self%dim
       call init_type(self%ivec(i),xl(i),xu(i),tol_(i),method_(i))
    enddo
    !
    call dgauss_generic(self%ivec(self%dim),ans,ierr_,err_,m)
    select case(ierr_)
    case(-1)
       stop "Lower Bound ~ Upper Bound => Ans set to zero"
    case(2)
       stop "Dgauss_Generic ABNORMAL EXIT CODE: ANS does not meet requested tolerance"
    case default
       continue
    end select
    if(present(ierr))ierr=ierr_
    if(present(err))err=err_
    !
  contains
    !
    function f_of_x1(x1,m) result(f)
      real(8)                       :: x1
      integer                       :: m
      real(8)                       :: f(m)
      real(8),dimension(size(xl)-1) :: xvec
      integer                       :: i
      xvec = (/( self%ivec(i)%val, i=2,size(xl) )/)
      f = self%fxvec([x1,xvec],m)
    end function f_of_x1
    !
    function f_of_x2(x2,m) result(f)
      real(8) :: x2
      integer :: m
      real(8) :: f(m)
      self%ivec(2)%val = x2
      call dgauss_generic(self%ivec(1),f, ierr_, err_,m)
    end function f_of_x2
    !
    function f_of_x3(x3,m) result(f)
      real(8) :: x3
      integer :: m
      real(8) :: f(m)
      self%ivec(3)%val = x3
      call dgauss_generic(self%ivec(2),f, ierr_, err_,m)
    end function f_of_x3
    !
    function f_of_x4(x4,m) result(f)
      real(8) :: x4
      integer :: m
      real(8) :: f(m)
      self%ivec(4)%val = x4
      call dgauss_generic(self%ivec(3),f, ierr_, err_,m)
    end function f_of_x4
    !
    function f_of_x5(x5,m) result(f)
      real(8) :: x5
      integer :: m
      real(8) :: f(m)
      self%ivec(5)%val = x5
      call dgauss_generic(self%ivec(4),f, ierr_, err_,m)
    end function f_of_x5
    !
    function f_of_x6(x6,m) result(f)
      real(8) :: x6
      integer :: m
      real(8) :: f(m)
      self%ivec(6)%val = x6
      call dgauss_generic(self%ivec(5),f, ierr_, err_,m)
    end function f_of_x6
    !
  end subroutine integrate_nd_func_main



  subroutine integrate_nd_func_1(func,xl,xu,ans,methods,method,tols,tol,ierr,err)
    interface
       function func(x)
         real(8),dimension(:) :: x
         real(8)              :: func
       end function func
    end interface
    real(8),dimension(:),intent(in)                 :: xl       !! integration lower bounds
    real(8),dimension(size(xl)),intent(in)          :: xu       !! integration upper bounds
    real(8),intent(inout)                           :: ans
    integer,dimension(size(xl)),intent(in),optional :: methods  !! quadrature methods nD-->nD to use
    integer,intent(in),optional                     :: method   !! quadrature method   1-->nD to use
    real(8),dimension(size(xl)),intent(in),optional :: tols     !! error tolerances nD-->nD for dx integration
    real(8),intent(in),optional                     :: tol     !! error tolerance   1-->nD for dx integration
    integer,intent(out),optional                    :: ierr
    real(8),intent(out),optional                    :: err
    real(8),dimension(size(xl))                     :: tol_,ans_(1)
    integer,dimension(size(xl))                     :: method_
    integer                                         :: ierr_
    real(8)                                         :: err_
    !
    tol_   = 1d-9 ; if(present(tol))tol_ = tol; if(present(tols))tol_ = tols
    method_= 10   ; if(present(method))method_=method; if(present(methods))method_=methods
    call integrate_nd_func_main(1,fxvec,xl,xu,ans_,methods=method_,tols=tol_,ierr=ierr_,err=err_)
    ans = ans_(1)
    if(present(ierr))ierr=ierr_
    if(present(err))err=err_
    !
  contains
    !
    function fxvec(x,m)
      real(8),dimension(:) :: x
      integer              :: m
      real(8),dimension(m) :: fxvec
      fxvec(1) = func(x)
    end function fxvec
    !
  end subroutine integrate_nd_func_1






  !##################################################################
  !##################################################################
  !               SAMPLED FUNCTIONS: 1D + 2D R^n --> R
  !##################################################################
  !##################################################################
  subroutine integrate_1d_sample(fsample,xl,xu,ans,tol,method,ierr,err)
    real(8),dimension(:)             :: fsample       !! 1d array: f(x)
    real(8),intent(in)               :: xl       !! x integration lower bound
    real(8),intent(in)               :: xu       !! x integration upper bound
    real(8),intent(inout)            :: ans
    !
    real(8),intent(in),optional      :: tol     !! error tolerance for dx integration
    integer,intent(in),optional      :: method  !! quadrature method to use for x
    integer,intent(out),optional     :: ierr
    real(8),intent(out),optional     :: err
    !
    real(8)                          :: tol_,ans_(1)
    integer                          :: method_
    integer                          :: ierr_
    real(8)                          :: err_
    !
    type(finter1d_type)              :: Finterp
    real(8),dimension(size(fsample)) :: Xsample
    integer                          :: Lsample
    integer                          :: Ninterp
    !
    tol_   = 1d-9 ; if(present(tol))tol_ = tol
    method_= 10   ; if(present(method))method_=method
    !
    Lsample = size(fsample)
    Xsample = linspace(xl,xu,Lsample)
    call init_finter_1d(Finterp,Xsample,fsample,method_)
    call integrate_1d_func_main(1,fx,xl,xu,ans_,tol_,method_,ierr_,err_)
    ans = ans_(1)
    if(present(ierr))ierr=ierr_
    if(present(err))err=err_
    !
  contains
    !
    function fx(x,m)
      real(8) :: x
      integer :: m
      real(8) :: fx(m)
      real(8) :: dy
      integer :: j,k,k0,k1
      integer :: n
      fx(1) = 0d0
      !
      N = Finterp%N    !order of polynomial interpolation
      j = locate(finterp%X(finterp%Imin:finterp%Imax),x)
      !
      k=max(j-(N-1)/2,1)
      k0=k
      if(k0 < finterp%Imin)k0=finterp%Imin
      k1=k+N+1
      if(k1 > finterp%Imax)then
         k1=finterp%Imax
         k0=k1-N-1
      endif
      call polint(finterp%X(k0:k1),finterp%F(k0:k1),x,fx(1),dy)
    end function fx
  end subroutine integrate_1d_sample


  subroutine integrate_2d_sample(fsample,xl,xu,ans,methods,method,tols,tol,ierr,err)
    real(8),dimension(:,:),intent(in)               :: fsample  !! 2d function: f(x,y)
    real(8),dimension(:),intent(in)                 :: xl       !! integration lower bounds
    real(8),dimension(size(xl)),intent(in)          :: xu       !! integration upper bounds
    real(8),intent(inout)                           :: ans
    !
    integer,dimension(size(xl)),intent(in),optional :: methods  !! quadrature methods nD-->nD to use
    integer,intent(in),optional                     :: method   !! quadrature method   1-->nD to use
    real(8),dimension(size(xl)),intent(in),optional :: tols     !! error tolerances nD-->nD for dx integration
    real(8),intent(in),optional                     :: tol     !! error tolerance   1-->nD for dx integration
    integer,intent(out),optional                    :: ierr
    real(8),intent(out),optional                    :: err
    !
    real(8),dimension(size(xl))                     :: tol_,ans_(1)
    integer,dimension(size(xl))                     :: method_
    integer                                         :: ierr_
    real(8)                                         :: err_
    !
    type(finter2d_type)                             :: Finterp
    real(8),dimension(size(fsample,1))              :: Xsample
    real(8),dimension(size(fsample,2))              :: Ysample
    integer                                         :: Lsample1,Lsample2
    integer                                         :: Ninterp
    !
    tol_   = 1d-9 ; if(present(tol))tol_ = tol; if(present(tols))tol_ = tols
    method_= 10   ; if(present(method))method_=method; if(present(methods))method_=methods
    !
    Lsample1 = size(fsample,1)
    Lsample2 = size(fsample,2)
    Xsample  = linspace(xl(1),xu(1),Lsample1)
    Ysample  = linspace(xl(2),xu(2),Lsample2)
    call init_finter_2d(Finterp,Xsample,Ysample,Fsample,method_(1))
    call integrate_nd_func_main(1,fxvec,xl,xu,ans_,methods=method_,tols=tol_,ierr=ierr_,err=err_)
    ans = ans_(1)
    if(present(ierr))ierr=ierr_
    if(present(err))err=err_
    !
  contains
    !
    function fxvec(xvec,m)
      real(8),dimension(:) :: xvec
      integer              :: m
      real(8)              :: x,y
      real(8)              :: fxvec(m)
      real(8)              :: df
      integer              :: itmp,jtmp,kx,ky,k0x,k0y,k1x,k1y
      integer              :: n
      !
      x = xvec(1)
      y = xvec(2)
      !
      N=Finterp%N    !order of polynomial interpolation
      itmp=locate(Finterp%X(Finterp%Imin:Finterp%Imax),x)
      jtmp=locate(Finterp%Y(Finterp%Jmin:Finterp%Jmax),y)
      kx=max(itmp-(N-1)/2,1)
      ky=max(jtmp-(N-1)/2,1)
      k0x = kx ; if(k0x < Finterp%Imin)k0x=Finterp%Imin
      k0y = ky ; if(k0y < Finterp%Jmin)k0y=Finterp%Jmin
      k1x = kx+N+1
      if(k1x > Finterp%Imax)then         
         k1x=Finterp%Imax
         k0x=k1x-N-1
      endif
      k1y = ky+N+1
      if(k1y > Finterp%Jmax)then
         k1y=Finterp%Jmax
         k0y=k1y-N-1
      endif
      call polin2(Finterp%X(k0x:k1x),Finterp%Y(k0y:k1y),Finterp%F(k0x:k1x,k0y:k1y),x,y,fxvec(1),df)
    end function fxvec
    !
  end subroutine integrate_2d_sample




















  !  Integrate a real function of one variable over a finite
  !  interval using the specified adaptive algorithm.
  !  Intended primarily for high accuracy
  !  integration or integration of smooth functions.
  !  * SLATEC is public domain software: http://www.netlib.org/slatec/guide
  !  * Original sourcecode from: http://www.netlib.org/slatec/src/dgaus8.f
  !  * Jones, R. E., (SNLA) -- Original SLATEC code.
  !  * Jacob Williams : 1/20/2020 : refactored to modern Fortran and generalized.
  !@note This function is recursive. [It can call itself indirectly during double integration]
  !! pick a value for abs(error_tol) so that
  !! dtol < abs(error_tol) <= 1.0e-3 where dtol is the larger
  !! of 1.0e-18 and the real(8) unit roundoff d1mach(4).
  !! ans will normally have no more error than abs(error_tol)
  !! times the integral of the absolute value of fun(x).  usually,
  !! smaller values of error_tol yield more accuracy and require
  !! more function evaluations.
  recursive subroutine dgauss_generic(self, ans, ierr, err, m)
    type(integration_type),intent(inout) :: self
    real(8)                              :: lb         !! lower bound of the integration
    real(8)                              :: ub         !! upper bound of the integration
    real(8)                              :: error_tol  !! is a requested pseudorelative error tolerance.
    integer                              :: m
    real(8),intent(out),dimension(m)     :: ans        !! computed value of integral
    integer,intent(out)                  :: ierr       !! status code:
    real(8),intent(out)                  :: err        !! an estimate of the absolute error in `ans`.
    !
    real(8),parameter                    :: sq2      = sqrt(two)
    real(8),parameter                    :: ln2      = log(two)
    integer,parameter                    :: nlmn     = 1                   !! ??
    integer,parameter                    :: kmx      = 5000                !! ??
    integer,parameter                    :: kml      = 6                   !! ??
    real(8),parameter                    :: magic    = 0.30102000d0       !! ??
    integer,parameter                    :: iwork    = 60                  !! size of the work arrays. ?? Why 60 ??
    real(8),parameter                    :: bb       = radix(one)          !! machine constant
    real(8),parameter                    :: d1mach4  = bb**(1-digits(one)) !! machine constant
    real(8),parameter                    :: d1mach5  = log10(bb)           !! machine constant
    integer                              :: k,l,lmn,lmx,mxl,nbits,nib,nlmx
    real(8)                              :: ae(m),anib,area(m),c,ee(m),ef,eps,est(m),gl(m),glr(m),tol,merr(m)
    real(8),dimension(iwork)             :: aa,hh
    real(8),dimension(iwork,m)           :: gr,vl
    integer,dimension(iwork)             :: lr
    !
    lb = self%a
    ub = self%b
    error_tol = self%tol
    !
    ans = zero
    ierr = 1
    err = zero
    if (lb == ub) return
    aa = zero
    hh = zero
    vl = zero
    gr = zero
    lr = 0
    merr = zero
    k = digits(one)
    anib = d1mach5*k/magic
    nbits = anib
    nlmx = min(60,(nbits*5)/8)         ! ... is this the same 60 as iwork???
    lmx = nlmx
    lmn = nlmn
    if (ub /= zero) then
       if (sign(one,ub)*lb > zero) then
          c = abs(one-lb/ub)
          if (c <= 0.1d0) then
             if (c <= zero) return
             anib = one_half - log(c)/ln2
             nib = anib
             lmx = min(nlmx,nbits-nib-7)
             if (lmx < 1) then
                ! lb and ub are too nearly equal to allow
                ! normal integration [ans is set to zero]
                ierr = -1
                return
             end if
             lmn = min(lmn,lmx)
          end if
       end if
    end if
    tol = max(abs(error_tol),two**(5-nbits))/two
    if (error_tol == zero) tol = sqrt(d1mach4)
    eps = tol
    hh(1) = (ub-lb)/four
    aa(1) = lb
    lr(1) = 1
    l = 1
    est = self%g(aa(l)+two*hh(l),two*hh(l),m)
    k = 8
    area = abs(est)
    ef = one_half
    mxl = 0
    !compute refined estimates, estimate the error, etc.
    main : do
       gl = self%g(aa(l)+hh(l),hh(l),m)
       gr(l,:) = self%g(aa(l)+three*hh(l),hh(l),m)
       k = k + 16
       area = area + (abs(gl)+abs(gr(l,:))-abs(est))
       glr = gl + gr(l,:)
       ee = abs(est-glr)*ef
       ae = max(eps*area,tol*abs(glr))
       if(any(ee-ae > zero)) then
          !consider the left half of this level
          if (k > kmx) lmx = kml
          if (l >= lmx) then
             mxl = 1
          else
             l = l + 1
             eps = eps*one_half
             ef = ef/sq2
             hh(l) = hh(l-1)*one_half
             lr(l) = -1
             aa(l) = aa(l-1)
             est = gl
             cycle main
          end if
       end if
       merr = merr + (est-glr)
       if (lr(l) > 0) then
          !return one level
          ans = glr
          do
             if (l <= 1) exit main ! finished
             l = l - 1
             eps = eps*two
             ef = ef*sq2
             if (lr(l) <= 0) then
                vl(l,:) = vl(l+1,:) + ans
                est = gr(l-1,:)
                lr(l) = 1
                aa(l) = aa(l) + four*hh(l)
                cycle main
             end if
             ans = vl(l+1,:) + ans
          end do
       else
          !proceed to right half at this level
          vl(l,:) = glr
          est = gr(l-1,:)
          lr(l) = 1
          aa(l) = aa(l) + four*hh(l)
          cycle main
       end if
    end do main
    err = maxval(abs(merr))
    if ((mxl/=0) .and. any(abs(merr)-two*tol*area > 0)) ierr = 2 ! ans is probably insufficiently accurate
  end subroutine dgauss_generic




  !>  !### See also
  !  * Coefficients from:
  !    http://processingjs.nihongoresources.com/bezierinfo/legendre-gauss-values.php
  !  6-point method.
  recursive function g6(self, x, h, m) result(f)
    class(integration_type),intent(inout) :: self
    real(8), intent(in)                   :: x
    real(8), intent(in)                   :: h
    integer                               :: m
    real(8)                               :: f(m)
    !> abscissae:
    real(8),dimension(3),parameter ::  a = [   0.6612093864662645136613995950199053470064485643&
         &951700708145267058521834966071431009442864037464&
         &614564298883716392751466795573467722253804381723&
         &198010093367423918538864300079016299442625145884&
         &9024557188219703863032236201173523213570221879361&
         &8906974301231555871064213101639896769013566165126&
         &1150514997832d0,&
         &0.2386191860831969086305017216807119354186106301&
         &400213501813951645742749342756398422492244272573&
         &491316090722230970106872029554530350772051352628&
         &872175189982985139866216812636229030578298770859&
         &440976999298617585739469216136216592222334626416&
         &400139367778945327871453246721518889993399000945&
         &408150514997832d0,&
         &0.9324695142031520278123015544939946091347657377&
         &122898248725496165266135008442001962762887399219&
         &259850478636797265728341065879713795116384041921&
         &786180750210169211578452038930846310372961174632&
         &524612619760497437974074226320896716211721783852&
         &305051047442772222093863676553669179038880252326&
         &771150514997832d0 ]
    !> weights:
    real(8),dimension(3),parameter ::  w = [   0.36076157304813860756983351383771611166152189274&
         &674548228973924023714003783726171832096220198881&
         &934794311720914037079858987989027836432107077678&
         &721140858189221145027225257577711260007323688285&
         &916316028951118005174081368554707448247248610118&
         &325993144981721640242558677752676819993095031068&
         &73150514997832d0,&
         0.46791393457269104738987034398955099481165560576&
         &921053531162531996391420162039812703111009258479&
         &198230476626878975479710092836255417350295459356&
         &355927338665933648259263825590180302812735635025&
         &362417046193182590009975698709590053347408007463&
         &437682443180817320636917410341626176534629278889&
         &17150514997832d0,&
         0.17132449237917034504029614217273289352682250148&
         &404398239863543979894576054234015464792770542638&
         &866975211652206987440430919174716746217597462964&
         &922931803144845206713510916832108437179940676688&
         &721266924855699404815942932735702498405343382418&
         &236324411837461039120523911904421970357029774978&
         &12150514997832d0 ]
    f = h * ( w(1)*( self%fx(x-a(1)*h,m) + self%fx(x+a(1)*h,m) ) + &
         w(2)*( self%fx(x-a(2)*h,m) + self%fx(x+a(2)*h,m) ) + &
         w(3)*( self%fx(x-a(3)*h,m) + self%fx(x+a(3)*h,m) ) )
  end function g6


  !  This is the 8-point formula from the original SLATEC routine
  recursive function g8(self, x, h,m ) result(f)
    class(integration_type),intent(inout) :: self
    real(8), intent(in)                   :: x
    real(8), intent(in)                   :: h
    integer                               :: m
    real(8)                               :: f(m)
    !> abscissae:
    real(8),parameter ::   x1 = 0.18343464249564980493947614236018398066675781291297378231718847&
         &369920447422154211411606822371112335374526765876&
         &428676660891960125238768656837885699951606635681&
         &044755516171385019663858107642055323708826547494&
         &928123149612477646193635627706457164566131594051&
         &34052985058171969174306064445289638150514997832d0
    real(8),parameter ::   x2 = 0.52553240991632898581773904918924634904196424312039285775085709&
         &927245482076856127252396140019363198206190968292&
         &482526085071087937666387799398053953036682536311&
         &190182730324023600607174700061279014795875767562&
         &412888953366196435283308256242634705401842246036&
         &88817537938539658502113876953598879150514997832d0
    real(8),parameter ::   x3 = 0.79666647741362673959155393647583043683717173161596483207017029&
         &503921730567647309214715192729572593901919745345&
         &309730926536564949170108596027725620746216896761&
         &539350162903423256455826342053015458560600957273&
         &426035574157612651404288519573419337108037227831&
         &36113628137267630651413319993338002150514997832d0
    real(8),parameter ::   x4 = 0.96028985649753623168356086856947299042823523430145203827163977&
         &737242489774341928443943895926331226831042439281&
         &729417621023895815521712854793736422049096997004&
         &339826183266373468087812635533469278673596634808&
         &705975425476039293185338665681328688426134748962&
         &8923208763998895240977248938732425615051499783203d0
    !> weights:
    real(8),parameter ::   w1 = 0.36268378337836198296515044927719561219414603989433054052482306&
         &756668673472390667732436604208482850955025876992&
         &629670655292582155698951738449955760078620768427&
         &783503828625463057710075533732697147148942683287&
         &804318227790778467229655355481996014024877675059&
         &28976560993309027632737537826127502150514997832d0
    real(8),parameter ::   w2 = 0.31370664587788728733796220198660131326032899900273493769026394&
         &507495627194217349696169807623392855604942757464&
         &107780861624724683226556160568906242764697589946&
         &225031187765625594632872220215204316264677947216&
         &038226012952768986525097231851579983531560624197&
         &51736972560423953923732838789657919150514997832d0
    real(8),parameter ::   w3 = 0.22238103445337447054435599442624088443013087005124956472590928&
         &929361681457044904085365314237719792784215926610&
         &121221812311143757985257224193818266745320905779&
         &086132895368404027893986488760043856972021574820&
         &632532471955902286315706513199655897335454406059&
         &52819880671616779621183704306688233150514997832d0
    real(8),parameter ::   w4 = 0.10122853629037625915253135430996219011539409105168495705900369&
         &806474017876347078486028273930404500655815438933&
         &141326670771549403089234876787319730411360735846&
         &905332088240507319763065757292054679614357794675&
         &524923287300550259929540899466768105108107294683&
         &66466585774650346143712142008566866150514997832d0
    f = h * ( w1*( self%fx(x-x1*h,m) + self%fx(x+x1*h,m)) + &
         w2*( self%fx(x-x2*h,m) + self%fx(x+x2*h,m)) + &
         w3*( self%fx(x-x3*h,m) + self%fx(x+x3*h,m)) + &
         w4*( self%fx(x-x4*h,m) + self%fx(x+x4*h,m)) )
  end function g8




  !  10-point method.
  recursive function g10(self, x, h, m) result(f)
    class(integration_type),intent(inout) :: self
    real(8), intent(in)                   :: x
    real(8), intent(in)                   :: h
    integer                               :: m
    real(8)                               :: f(m)
    !> abscissae:
    real(8),dimension(5),parameter ::  a = [   0.14887433898163121088482600112971998461756485942&
         &069169570798925351590361735566852137117762979946&
         &369123003116080525533882610289018186437654023167&
         &619699680909130507378277203710590709424758594227&
         &432498371771742473462169148529029429290031934666&
         &590824338380943550759968335702300050038372806343&
         &51d0,&
         0.43339539412924719079926594316578416220007183765&
         &624649650270151314376698907770350122510275795011&
         &772122368293504099893794727422475772324920512677&
         &410328220862009523192709334620320113283203876915&
         &840634111498011298231414887874432043247664144215&
         &76788807708483879452488118549797039287926964254222d0,&
         0.67940956829902440623432736511487357576929471183&
         &480946766481718895255857539507492461507857357048&
         &037949983390204739931506083674084257663009076827&
         &417182029235431978528469774097183691437120135529&
         &628377331531086791269325449548547293413247272116&
         &80274268486617121011712030227181051010718804444161d0,&
         0.86506336668898451073209668842349304852754301496&
         &533045252195973184537475513805556135679072894604&
         &577069440463108641176516867830016149345356373927&
         &293968909500115713496898930516120724357604809009&
         &797259233179237955357392905958797769568324277022&
         &36942765911483643714816923781701572597289139322313d0,&
         0.97390652851717172007796401208445205342826994669&
         &238211923121206669659520323463615962572356495626&
         &855625823304251877421121502216860143447777992054&
         &095872599424367044136957648812587991466331435107&
         &587371198778752105670674524353687136830338609093&
         &88311646653581707125686970668737259229449284383797d0 ]
    !> weights:
    real(8),dimension(5),parameter ::  w = [   0.29552422471475287017389299465133832942104671702&
         &685360135430802975599593821715232927035659579375&
         &421672271716440125255838681849078955200582600193&
         &634249418696660956271864888416804323130506153586&
         &740908305127066386528748390174687472659751595445&
         &0775158914556548308329986393605934912382356670244d0,&
         0.26926671930999635509122692156946935285975993846&
         &088379580056327624215343231917927676422663670925&
         &276075559581145036869830869292346938114524155646&
         &588466344237116560144322599601417290445280303444&
         &112979029770671425375348062846083992765750069116&
         &86749842814086288868533208042150419508881916391898d0,&
         0.21908636251598204399553493422816319245877187052&
         &267708988095654363519991065295128124268399317720&
         &219278659121687281288763476662690806694756883092&
         &118433166566771052699153220775367726528266710278&
         &782468510102088321733200642734832547562506684158&
         &85349420711613410227291565477768928313300688702802d0,&
         0.14945134915058059314577633965769733240255663966&
         &942736783547726875323865472663001094594726463473&
         &195191400575256104543633823445170674549760147137&
         &160119371095287981348288651187709535664396393337&
         &739399092016902046490838156187791575225783003434&
         &27785361756927642128792412282970150172590842897331d0,&
         0.06667134430868813759356880989333179285786483432&
         &015814512869488161341206408408710177678550968505&
         &887782109005471452041933148750712625440376213930&
         &498731699404163449536370640018701124231550439352&
         &624245062983271819871864748056604411786208647844&
         &9236378557180717569208295026105115288152794421677d0 ]
    f = h * ( w(1)*(  self%fx(x-a(1)*h,m)   +  self%fx(x+a(1)*h,m) ) + &
         w(2)*(  self%fx(x-a(2)*h,m)   +  self%fx(x+a(2)*h,m) ) + &
         w(3)*(  self%fx(x-a(3)*h,m)   +  self%fx(x+a(3)*h,m) ) + &
         w(4)*(  self%fx(x-a(4)*h,m)   +  self%fx(x+a(4)*h,m) ) + &
         w(5)*(  self%fx(x-a(5)*h,m)   +  self%fx(x+a(5)*h,m) )   )
  end function g10


  !  12-point method.
  recursive function g12(self, x, h, m) result(f)
    class(integration_type),intent(inout) :: self
    real(8), intent(in)                   :: x
    real(8), intent(in)                   :: h
    integer                               :: m
    real(8)                               :: f(m)
    !> abscissae:
    real(8),dimension(6),parameter ::  a = [   0.12523340851146891547244136946385312998339691630&
         &544427321292175474846205624138968874286829846949&
         &135959410459879132051097315159969664463407959720&
         &578930281363427149751877364610797786290401085851&
         &749803458163536009061915338533985792224380950454&
         &5097342064247739686883799517760948964137522919201d0,&
         0.36783149899818019375269153664371756125636014133&
         &540962131179987950408992951678787387873442850054&
         &657723463312639597714521513515217932743935324199&
         &163774275382871320389664162274303718284470963188&
         &934547884841822611461227526979609371629600504639&
         &62319787423676668046033025242558536362617894366679d0,&
         0.58731795428661744729670241894053428036909851404&
         &805248151027087966734069937589526243571076498874&
         &820190960155999292889267723106959108867175142499&
         &189843704151965799654931521792486834699342245746&
         &542270556959107871794349154143635139191674285545&
         &96877940491139756923177447689738849120865435563147d0,&
         0.76990267419430468703689383321281807598492575001&
         &893163766441906424911654310847122401642499922342&
         &191061761754045422185620704016285265354759491942&
         &035158754711514435184626896570143367857869960707&
         &068262822102488760216156789235759062543109515384&
         &10899341797549230707021382467596975621464477134163d0,&
         0.90411725637047485667846586611909619253759670921&
         &329754655407576068123479572923579048696942782373&
         &326781186038289641042234889971981954299601063524&
         &901258268291998347354448614206140899100247009682&
         &576258221693446448698746167580757842398074380920&
         &64065954540171679180850205196702894963912359448494d0,&
         0.98156063424671925069054909014928082296015519981&
         &373151046268212180779324431825398222525726789045&
         &223578555649237284127318524545703044707716708276&
         &967488752886112565550184482662910041202137201539&
         &996961235882788466302337187351583920530374414763&
         &9383170419389543470920618543180673569225988370568d0]
    !> weights:
    real(8),dimension(6),parameter ::  w = [   0.24914704581340278500056243604295121083046090256&
         &961883139535100311627942745728804303115680061804&
         &235306483347611787718583305851107360364968803964&
         &210377008542941502737221091728257019684301646591&
         &924021619820796255207324340857766137885796625403&
         &29347837170742904111565650371846972323325015720931d0,&
         0.23349253653835480876084989892487805625940997219&
         &975487473052349782149200007941167528067902650856&
         &369046673875643970886883389854278840891609661975&
         &038847380753533248145179488750388812162792803042&
         &489598308782293577290791644231030018795306547073&
         &15375809270840669989018891281956753131165193423269d0,&
         0.20316742672306592174906445580979837650651814727&
         &459014639859456579764563251047284379514439506460&
         &523243116042933686325996496137135190210132907910&
         &420189599423685656890245260738280276852445703846&
         &681240064758134063899875305215261728059344541572&
         &2327927963339557545261423500783899286052850767594d0,&
         0.16007832854334622633465252954335907187201173049&
         &086417790989954415795422517329115068165655263705&
         &773052707487709681280262724376386088264904467503&
         &100243409511213679869020659979278560098046375913&
         &998387244938872586336059061667742863824552984448&
         &70458396283884610940466728874776625823124924247387d0,&
         0.10693932599531843096025471819399622421457017347&
         &032488000512604210281899362749757654053731809631&
         &645741357635933314114416117033051696355084484800&
         &865232691960050114390447649204829355515358576079&
         &107052492180710337954704248957128309309678064675&
         &98358517298903536137451828089012822811396588037254d0,&
         0.04717533638651182719461596148501706031702907399&
         &484708956050534700380972115203871067082590707541&
         &453609661610167559673857966748040823913299673846&
         &365109909808575797967885849598965975687054894525&
         &799700269519193179311245399071070942125321236826&
         &63180160342232703368882666374567833050364187887189d0]
    f = h * ( w(1)*(  self%fx(x-a(1)*h,m)   +  self%fx(x+a(1)*h,m) ) + &
         w(2)*(  self%fx(x-a(2)*h,m)   +  self%fx(x+a(2)*h,m) ) + &
         w(3)*(  self%fx(x-a(3)*h,m)   +  self%fx(x+a(3)*h,m) ) + &
         w(4)*(  self%fx(x-a(4)*h,m)   +  self%fx(x+a(4)*h,m) ) + &
         w(5)*(  self%fx(x-a(5)*h,m)   +  self%fx(x+a(5)*h,m) ) + &
         w(6)*(  self%fx(x-a(6)*h,m)   +  self%fx(x+a(6)*h,m) ) )
  end function g12



  !>
  !  14-point method.
  recursive function g14(self, x, h, m) result(f)
    class(integration_type),intent(inout) :: self
    real(8), intent(in)                   :: x
    real(8), intent(in)                   :: h
    integer                               :: m
    real(8)                               :: f(m)
    !> abscissae:
    real(8),dimension(7),parameter ::  a = [  0.10805494870734366206624465021983474761195160547&
         &4237557040821061308013529011730007130100688176689&
         &3672374502026424466474638099232632258191427567218&
         &1973150409752806137273842265069487944308775321508&
         &8445556391329819060204836416480024319739665907101&
         &2506161702814425014635643221773541001328892761d0,&
         &0.31911236892788976043567182416847546683426120353&
         &3843956596650187257333440512792783164933705421346&
         &4131802793151826090394496145640578710017716508863&
         &2222396245608012120993128542172348808287716458637&
         &8479374239121304478425121768114783511643536777896&
         &2949997448460558214759676525644841351801594858d0,&
         &0.51524863635815409196529071855118866230888528256&
         &9306036951504769092784951832055660452072020350772&
         &8923922907932905090138695274035571340047593918260&
         &5653057211011637652073200342580823038532041784020&
         &3436173906624491224801618641571038235567674745455&
         &3979637438627635490786064892912451481973721288d0,&
         &0.68729290481168547014801980301933413753840121274&
         &7170675619266488628184896183133256947373070505211&
         &8384106603630216790054729627432715418501010682124&
         &6881727389082952662885443589912839338608106959371&
         &4595904926885388784713769175169784875289055161406&
         &7877996475717650653147982694804026342351254071d0,&
         &0.82720131506976499318979474265039496103970110147&
         &5081181560709054241479830810028873570426390137889&
         &5453991241406273986535333275661226737816179582645&
         &1069907936808669317564778014567859855078251147291&
         &5830426696849656086721489336979443959282673643228&
         &6425172143208924251106624044295037127737490111d0,&
         &0.92843488366357351733639113937787426447703921040&
         &9837618717962447482131093544359853111413905683657&
         &5176363551261559882603607008578010786539258018984&
         &5400440650494157888098179531161147719130825235345&
         &8596605653673043686690855550898698329741248613224&
         &5749388483890945436457404705549484348178721002d0,&
         &0.98628380869681233884159726670405280167609140723&
         &9225881644070811777749554132491637910646239665151&
         &7527602612562941358578689852603067447974494119727&
         &0324710898207170072955675048180261687970555989447&
         &5396929426197069500447181272675429908986256542893&
         &3676463914802477677291745002965827767360741735d0 ]
    !> weights:
    real(8),dimension(7),parameter ::  w = [   0.21526385346315779019587644331626003527499755805&
         &4128800219776392543618787353994604001024441410819&
         &5782372566723324367709929481659764649301890356019&
         &0805098142804175780269156508228762641736544919294&
         &6281203662033345376460522564310634412912654698349&
         &487266562730897512393716549425155133887783267d0,&
         &0.20519846372129560396592406566121805571033906130&
         &9419451716897290283367144825249720339431839991890&
         &8957243692694424494287284534856133850644865918702&
         &3021403166714178733299347482783913811132568481282&
         &5439676020905052976535424973123755325146919285189&
         &8072394707049964721031773292256965337005468577d0,&
         &0.18553839747793781374171659012515703624892260293&
         &7331659020034925069098350263525444425552731146712&
         &2229825611215057289188990778964974252160895085525&
         &2415283643607286404060027232379697141385075345609&
         &3331227890449938852384485366393922617921879824760&
         &6150274514935557012909889503067356410067833406d0,&
         &0.15720316715819353456960193862384215660566803733&
         &7323374969317043874768176369608298513958093362418&
         &0762768531519990811885018854374920646576267489242&
         &9103726460198700102219564745910784232280561068611&
         &6907713218466935160138377442838502265889923868443&
         &9084685022864905124096570215866733146092008329d0,&
         &0.12151857068790318468941480907247662595666934569&
         &0074672291075392543159743892526492318819906270375&
         &0071489155506530592569942811574313408868548096421&
         &2571445460802891854106154207862005646754562932960&
         &2540610239636717985405900755004972904989241013019&
         &1072357341821083329663867464821867539341968434d0,&
         &0.08015808715976020980563327706285430958369778539&
         &4594765201399065489571474457287169863536190819137&
         &7559686225015908038847487953091382572604434376755&
         &1198447409477973877237005366105771785226539545491&
         &2313554662497115946457665357652160093748935412771&
         &0937535198838649279475628473516378736712929573d0,&
         &0.03511946033175186303183287613819178061970560927&
         &7127276581499890196416322837808270537676796998646&
         &4636614217324764405511345585478510619843098677334&
         &0884595716394793248808744456729064741484147706750&
         &3186014306010893702617623540676052379390445897465&
         &9810087587180865408885105556219147609526200925d0 ]
    f = h * ( w(1)*(  self%fx(x-a(1)*h,m)   +  self%fx(x+a(1)*h,m) ) + &
         w(2)*(  self%fx(x-a(2)*h,m)   +  self%fx(x+a(2)*h,m) ) + &
         w(3)*(  self%fx(x-a(3)*h,m)   +  self%fx(x+a(3)*h,m) ) + &
         w(4)*(  self%fx(x-a(4)*h,m)   +  self%fx(x+a(4)*h,m) ) + &
         w(5)*(  self%fx(x-a(5)*h,m)   +  self%fx(x+a(5)*h,m) ) + &
         w(6)*(  self%fx(x-a(6)*h,m)   +  self%fx(x+a(6)*h,m) ) + &
         w(7)*(  self%fx(x-a(7)*h,m)   +  self%fx(x+a(7)*h,m) ) )
  end function g14








  !*******************************************************************
  !*******************************************************************
  !*******************************************************************






  subroutine init_finter_1d(self,Xin,Fin,N)
    type(finter1d_type) :: self
    real(8)           :: xin(:)
    real(8)           :: fin(size(xin))
    integer           :: N,Lin
    if(self%status)call delete_finter_1d(self)
    Lin=size(xin)
    allocate(self%x(Lin),self%f(Lin))
    self%X    = Xin
    self%F    = Fin
    self%Imax = Lin
    self%Imin = 1
    self%N    = N
    self%status=.true.
  end subroutine init_finter_1d

  subroutine init_finter_2d(self,Xin,Yin,Fin,N)
    type(finter2d_type) :: self
    real(8)       :: xin(:),yin(:)
    real(8)       :: fin(size(xin),size(yin))
    integer       :: N,Lx,Ly
    if(self%status)call delete_finter_2d(self)
    Lx=size(xin) ; Ly=size(yin)
    allocate(self%x(Lx),self%y(Ly),self%f(Lx,Ly))
    self%X    = Xin
    self%Y    = Yin
    self%F    = Fin
    self%Imin = 1
    self%Jmin = 1
    self%Imax = Lx
    self%Jmax = Ly
    self%N    = N
    self%status=.true.
  end subroutine init_finter_2d

  subroutine delete_finter_1d(self)
    type(finter1d_type) :: self
    if(allocated(self%X))deallocate(self%X)
    if(allocated(self%F))deallocate(self%F)
    self%imax=0
    self%imin=0
    self%N   =0
    self%status=.false.
  end subroutine delete_finter_1d

  subroutine delete_finter_2d(self)
    type(finter2d_type) :: self
    if(allocated(self%x))deallocate(self%x)
    if(allocated(self%y))deallocate(self%y)
    if(allocated(self%f))deallocate(self%f)
    self%imin=0
    self%jmin=0
    self%imax=0
    self%jmax=0
    self%N   =0
    self%status=.false.
  end subroutine delete_finter_2d


  function locate(xx,x)
    real(8), dimension(:), intent(in) :: xx
    real(8), intent(in)               :: x
    integer                           :: locate
    integer                           :: n,jl,jm,ju
    logical                           :: ascnd
    n=size(xx)
    ascnd = (xx(n) >= xx(1))
    jl=0
    ju=n+1
    do
       if (ju-jl <= 1) exit
       jm=(ju+jl)/2
       if (ascnd .eqv. (x >= xx(jm))) then
          jl=jm
       else
          ju=jm
       end if
    end do
    if (x == xx(1)) then
       locate=1
    else if (x == xx(n)) then
       locate=n-1
    else
       locate=jl
    end if
  end function locate


  subroutine polint(xa,ya,x,y,dy)
    real(8), dimension(:), intent(in) :: xa,ya
    real(8), intent(in)               :: x
    real(8), intent(out)              :: y,dy
    integer                           :: m,n,ns
    real(8), dimension(size(xa))      :: c,d,den,ho
    n=assert_eq(size(xa),size(ya),'polint')
    c=ya
    d=ya
    ho=xa-x
    ns=iminloc(abs(x-xa))
    y=ya(ns)
    ns=ns-1
    do m=1,n-1
       den(1:n-m)=ho(1:n-m)-ho(1+m:n)
       if (any(den(1:n-m) == 0.d0))then
          print*,'polint: calculation failure'
          stop
       endif
       den(1:n-m)=(c(2:n-m+1)-d(1:n-m))/den(1:n-m)
       d(1:n-m)=ho(1+m:n)*den(1:n-m)
       c(1:n-m)=ho(1:n-m)*den(1:n-m)
       if (2*ns < n-m) then
          dy=c(ns+1)
       else
          dy=d(ns)
          ns=ns-1
       end if
       y=y+dy
    end do
  end subroutine polint


  subroutine polin2(x1a,x2a,ya,x1,x2,y,dy)
    real(8), dimension(:), intent(in)   :: x1a,x2a
    real(8), dimension(:,:), intent(in) :: ya
    real(8), intent(in)                 :: x1,x2
    real(8), intent(out)                :: y,dy
    integer                             :: j,m,ndum
    real(8), dimension(size(x1a))       :: ymtmp
    real(8), dimension(size(x2a))       :: yntmp
    m = size(x1a);if(m/=size(ya,1))stop "POLINT: wrong dimensions m"
    ndum=size(x2a);if(ndum/=size(ya,2))stop "POLINT: wrong dimensions ndum"
    do j=1,m
       yntmp=ya(j,:)
       call polint(x2a,yntmp,x2,ymtmp(j),dy)
    end do
    call polint(x1a,ymtmp,x1,y,dy)
  end subroutine polin2


  function iminloc(arr)
    real(8), dimension(:), intent(in) :: arr
    integer, dimension(1) :: imin
    integer :: iminloc
    imin=minloc(arr(:))
    iminloc=imin(1)
  end function iminloc

  function assert_eq(n1,n2,string)
    character(len=*), intent(in) :: string
    integer, intent(in) :: n1,n2
    integer :: assert_eq
    if (n1 == n2) then
       assert_eq=n1
    else
       write (*,*) 'nrerror: an assert_eq failed with this tag:', &
            string
       stop 'program terminated by assert_eq'
    end if
  end function assert_eq




  function linspace(start,stop,num,istart,iend,mesh) result(array)
    integer          :: num,i
    real(8)          :: start,stop,step,array(num)
    logical,optional :: istart,iend
    logical          :: startpoint_,endpoint_
    real(8),optional :: mesh
    if(num<0)stop "linspace: N<0, abort."
    startpoint_=.true.;if(present(istart))startpoint_=istart
    endpoint_=.true.;if(present(iend))endpoint_=iend
    if(startpoint_.AND.endpoint_)then
       if(num<2)stop "linspace: N<2 with both start and end points"
       step = (stop-start)/real(num-1,8)
       forall(i=1:num)array(i)=start + real(i-1,8)*step
    elseif(startpoint_.AND.(.not.endpoint_))then
       step = (stop-start)/real(num,8)
       forall(i=1:num)array(i)=start + real(i-1,8)*step
    elseif(.not.startpoint_.AND.endpoint_)then
       step = (stop-start)/real(num,8)
       forall(i=1:num)array(i)=start + real(i,8)*step
    else
       step = (stop-start)/real(num+1,8)
       forall(i=1:num)array(i)=start + real(i,8)*step
    endif
    if(present(mesh))mesh=step
  end function linspace


END MODULE GAUSS_QUADRATURE