# stdlib_quadrature_gauss.f90 Source File

## Source Code

submodule (stdlib_quadrature) stdlib_quadrature_gauss
use stdlib_specialfunctions, only: legendre, dlegendre
implicit none

real(dp), parameter :: pi = acos(-1._dp)
real(dp), parameter :: tolerance = 4._dp * epsilon(1._dp)
integer, parameter :: newton_iters = 100

contains

pure module subroutine gauss_legendre_fp64 (x, w, interval)
real(dp), intent(out) :: x(:), w(:)
real(dp), intent(in), optional :: interval(2)

associate (n => size(x)-1 )
select case (n)
case (0)
x = 0
w = 2
case (1)
x(1) = -sqrt(1._dp/3._dp)
x(2) = -x(1)
w = 1
case default
block
integer :: i,j
real(dp) :: leg, dleg, delta

do i = 0, (n+1)/2 - 1
! use Gauss-Chebyshev points as an initial guess
x(i+1) = -cos((2*i+1)/(2._dp*n+2._dp) * pi)
do j = 1, newton_iters
leg  = legendre(n+1,x(i+1))
dleg = dlegendre(n+1,x(i+1))
delta = -leg/dleg
x(i+1) = x(i+1) + delta
if ( abs(delta) <= tolerance * abs(x(i+1)) )  exit
end do
x(n-i+1) = -x(i+1)

dleg = dlegendre(n+1,x(i+1))
w(i+1)   = 2._dp/((1-x(i+1)**2)*dleg**2)
w(n-i+1) = w(i+1)
end do

if (mod(n,2) == 0) then
x(n/2+1) = 0

dleg = dlegendre(n+1, 0.0_dp)
w(n/2+1) = 2._dp/(dleg**2)
end if
end block
end select
end associate

if (present(interval)) then
associate ( a => interval(1) , b => interval(2) )
x = 0.5_dp*(b-a)*x+0.5_dp*(b+a)
w = 0.5_dp*(b-a)*w
end associate
end if
end subroutine

pure module subroutine gauss_legendre_lobatto_fp64 (x, w, interval)
real(dp), intent(out) :: x(:), w(:)
real(dp), intent(in), optional :: interval(2)

associate (n => size(x)-1)
select case (n)
case (1)
x(1) = -1
x(2) =  1
w = 1
case default
block
integer :: i,j
real(dp) :: leg, dleg, delta

x(1)   = -1._dp
x(n+1) =  1._dp
w(1)   =  2._dp/(n*(n+1._dp))
w(n+1) =  2._dp/(n*(n+1._dp))

do i = 1, (n+1)/2 - 1
! initial guess from an approximate form given by SV Parter (1999)
x(i+1) = -cos( (i+0.25_dp)*pi/n  - 3/(8*n*pi*(i+0.25_dp)))
do j = 1, newton_iters
leg  = legendre(n+1,x(i+1)) - legendre(n-1,x(i+1))
dleg = dlegendre(n+1,x(i+1)) - dlegendre(n-1,x(i+1))
delta = -leg/dleg
x(i+1) = x(i+1) + delta
if ( abs(delta) <= tolerance * abs(x(i+1)) )  exit
end do
x(n-i+1) = -x(i+1)

leg = legendre(n, x(i+1))
w(i+1)   = 2._dp/(n*(n+1._dp)*leg**2)
w(n-i+1) = w(i+1)
end do

if (mod(n,2) == 0) then
x(n/2+1) = 0

leg = legendre(n, 0.0_dp)
w(n/2+1)   = 2._dp/(n*(n+1._dp)*leg**2)
end if
end block
end select
end associate

if (present(interval)) then
associate ( a => interval(1) , b => interval(2) )
x = 0.5_dp*(b-a)*x+0.5_dp*(b+a)
x(1)       = interval(1)
x(size(x)) = interval(2)
w = 0.5_dp*(b-a)*w
end associate
end if
end subroutine
end submodule