diff --git a/src/bsplines.f90 b/src/bsplines.f90
index 13bf402..16fcdf0 100644
--- a/src/bsplines.f90
+++ b/src/bsplines.f90
@@ -1,4285 +1,4285 @@
 !>
 !> @file bsplines.f90
 !>
 !> @brief
 !>
 !> @copyright
 !> Copyright (©) 2021 EPFL (Ecole Polytechnique Fédérale de Lausanne)
 !> SPC (Swiss Plasma Center)
 !>
 !> SPClibs is free software: you can redistribute it and/or modify it under
 !> the terms of the GNU Lesser General Public License as published by the Free
 !> Software Foundation, either version 3 of the License, or (at your option)
 !> any later version.
 !>
 !> SPClibs is distributed in the hope that it will be useful, but WITHOUT ANY
 !> WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 !> FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 !>
 !> You should have received a copy of the GNU Lesser General Public License
 !> along with this program. If not, see <https://www.gnu.org/licenses/>.
 !>
 !> @author
 !> (in alphabetical order)
 !> @author Stephan Brunner <stephan.brunner@epfl.ch>
 !> @author Trach-Minh Tran <trach-minh.tran@epfl.ch>
 !>
 MODULE bsplines
 !
 !    BSPLINES: A module to construct B-Splines of any order on
 !              non-equidistant mesh. Can be used for interpolation and
 !              Finite Element discretization.
 !
 !    T.M. Tran, S. Brunner, CRPP-EPFL
 !    February 2007
 !
   USE matrix
   IMPLICIT NONE
   PRIVATE
   PUBLIC :: spline1d, set_spline, get_dim, get_gauss, gridval
   PUBLIC :: spline2d, spline2d1d, def_knots, allsplines
   PUBLIC :: set_splcoef, get_splcoef
   PUBLIC :: fintg, calc_integ, destroy_sp
   PUBLIC :: gauleg, CompMassMatrix
   PUBLIC :: basfun_recur, basfun, def_basfun, is_equid, locintv_old, locintv
   PUBLIC :: calc_fftmass, calc_fftmass_old
   PUBLIC :: init_dft, ft_basfun
   PUBLIC :: getgrad
   PUBLIC :: dftmap
 !
   DOUBLE PRECISION, PARAMETER :: pi=3.1415926535897931d0
 !
   TYPE dftmap
      INTEGER :: n                                    ! Total number of modes
      INTEGER :: kmin, kmax                           ! Define the Foyrier window
      INTEGER :: dk                                   ! Number of modes in window
      DOUBLE PRECISION :: dx                          ! Interval in real space
      INTEGER, POINTER :: mode_couplings(:) => NULL() ! Table of mode couplings
      DOUBLE COMPLEX, POINTER :: coefs(:,:) => NULL() ! The restricted Fourier coefs
   END TYPE dftmap
 !
   TYPE spline1d
      INTEGER :: order                 ! Spline order = spline degree + 1
      INTEGER :: nints                 ! Number of knot intervals
      INTEGER :: nsites                ! Number of interpolation sites
      INTEGER :: dim                   ! Dimension of spline space
      INTEGER :: left=0                ! Save value used by routine LOCATE
      LOGICAL :: period                ! is the grid periodic ?
      LOGICAL :: nlequid               ! is the grid equidistant ?
      LOGICAL :: nlppform              ! Construct and build PPFORM in GRIDVAL if .TRUE.
      DOUBLE PRECISION :: lperiod      ! Periodicity
      DOUBLE PRECISION :: hinv         ! Inverse of mesh size, when nlequid=T
      INTEGER, POINTER ::&
           & fmap(:) => NULL()         ! Mapping of fine to coarse mesh
      DOUBLE PRECISION, POINTER :: &
           & knots(:) => NULL(), &     ! Spline knots (-p:n+p)
           & val0(:,:,:) => NULL(), &  ! Values and deriv. at left boundary of all splines 
           & valc(:,:) => NULL(), &    ! Values and deriv. at left boundary of equid. per. splines 
           & gausx(:,:) => NULL(), &   ! Gauss abscissas
           & gausw(:,:) => NULL(), &   ! Gauss weights
           & intspl(:) => NULL(), &    ! Integral of splines
           & ppform(:,:) => NULL(), &  ! PPFORM coefs
           & bcoefs(:) => NULL()       ! Spline coefs
      DOUBLE COMPLEX, POINTER :: &
           & ppformz(:,:) => NULL(), & ! PPFORM coefs for complex function
           & bcoefsc(:) => NULL()      ! Spline coefs for complex function
      TYPE(GBMAT) :: mat               ! Interpolation matrix
      TYPE(periodic_mat) :: matp       ! Interpolation matrix (periodic case)
      TYPE(dftmap) :: dft                ! Define DFT mapping
   END TYPE spline1d
 !
   TYPE spline2d
      TYPE(spline1d) :: sp1                                  ! Spline in direction 1
      TYPE(spline1d) :: sp2                                  ! Spline in direction 2
      DOUBLE PRECISION, POINTER :: ppform(:,:,:,:) => NULL() ! 2d PPFORM coefs
      DOUBLE PRECISION, POINTER :: bcoefs(:,:) => NULL()     ! Spline coefs
      DOUBLE COMPLEX, POINTER :: ppformz(:,:,:,:) => NULL()  ! PPFORM coefs for complex function
      DOUBLE COMPLEX, POINTER :: bcoefsc(:,:) => NULL()      ! Spline coefs for complex function
   END TYPE spline2d
 !
   TYPE spline2d1d
      TYPE(spline2d) :: sp12  ! 2D spline for dir. 1 and 2
      TYPE(spline1d) :: sp3   ! 1D spline for dir. 3
      DOUBLE PRECISION, POINTER :: ppform(:,:,:,:,:,:) => NULL() ! PPFORM coefs
      DOUBLE PRECISION, POINTER :: bcoefs(:,:,:) => NULL()       ! Spline coefs
      DOUBLE PRECISION, POINTER :: ppformz(:,:,:,:,:,:) => NULL()! PPFORM coefs for complex function
      DOUBLE COMPLEX, POINTER :: bcoefsc(:,:,:) => NULL()        ! Spline coefs for complex function
   END TYPE spline2d1d
 !
   INTERFACE set_spline
      MODULE PROCEDURE set_spline1d, set_spline2d, set_spline2d1d
   END INTERFACE
   INTERFACE get_dim
      MODULE PROCEDURE get_dim1, get_dim2
   END INTERFACE
   INTERFACE gridval
      MODULE PROCEDURE gridval1d, gridval1dz, &
           &           gridval2d, gridval2dz, &
           &           gridval2d_1d, gridval2d_1dz, &
           &           gridval2d_2d, gridval2d_2dz, &
           &           gridval2d1d_3d, gridval2d1d_1d 
   END INTERFACE
   INTERFACE set_splcoef
      MODULE PROCEDURE set_splcoef1d, set_splcoef2d
   END INTERFACE
   INTERFACE get_splcoef
      MODULE PROCEDURE get_splcoef1, get_splcoef1z, get_splcoefn, &
           &           get_splcoef2d, get_splcoef2dz
   END INTERFACE
   INTERFACE fintg
      MODULE PROCEDURE fintg1, fintg2
   END INTERFACE
   INTERFACE destroy_sp
      MODULE PROCEDURE destroy_sp1d, destroy_sp2d, destroy_sp2d1d
   END INTERFACE
   INTERFACE CompMassMatrix
      MODULE PROCEDURE CompMassMatrix1, CompMassMatrix_gb, CompMassMatrix_zgb
   END INTERFACE
   INTERFACE calc_integ
      MODULE PROCEDURE calc_integ0,calc_integn 
   END INTERFACE
   INTERFACE locintv
      MODULE PROCEDURE locintv0, locintv1
   END INTERFACE locintv
   INTERFACE locintv_old
      MODULE PROCEDURE locintv0_old, locintv1_old
   END INTERFACE locintv_old
   INTERFACE ppval
      MODULE PROCEDURE ppval0,  ppval1,  ppval2, &
           &           ppval0_n, &
           &           ppval0z, ppval1z, ppval2z, &
           &           ppval0z_n
   END INTERFACE ppval
   INTERFACE basfun
      MODULE PROCEDURE basfun0, basfun1
   END INTERFACE basfun
   INTERFACE ft_basfun
      MODULE PROCEDURE ft_basfun0, ft_basfun1
   END INTERFACE ft_basfun
   INTERFACE def_basfun
      MODULE PROCEDURE def_basfun0, def_basfun1
   END INTERFACE def_basfun
   INTERFACE getgrad
      MODULE PROCEDURE getgradr, getgradz
   END INTERFACE getgrad
 !
 CONTAINS
 !===========================================================================
   SUBROUTINE set_spline1d(p, ngauss, grid, sp, period, nlppform, nlequid)
 !
 !  Setup a spline
 !
     INTEGER, INTENT(in) :: p, ngauss
     DOUBLE PRECISION, INTENT(in)  :: grid(:)
     TYPE(spline1d), INTENT(out)   :: sp
     LOGICAL, OPTIONAL, INTENT(in) :: period
     LOGICAL, OPTIONAL, INTENT(in) :: nlppform
     LOGICAL, OPTIONAL, INTENT(in) :: nlequid
 !
     DOUBLE COMPLEX :: zc
     DOUBLE PRECISION :: leng, xp, factinv, h
     INTEGER :: order, nints, i, k
     DOUBLE PRECISION :: temp(1:p+1,0:p)
 !
 !  Order of splines
     order = p+1
     sp%order = order
 !
 !  Dimension of spline space
     nints = SIZE(grid)-1
     sp%nints = nints
     sp%period = .FALSE.
     IF( PRESENT(period) ) THEN 
        sp%period = period
        sp%lperiod = grid(nints+1) - grid(1)
     END IF
     sp%dim = nints+p
 !
 !  Use or not PPFORM
     sp%nlppform = .TRUE.
     IF( PRESENT(nlppform) ) THEN
        sp%nlppform = nlppform
     END IF
 !
 !  Determine sequence of knots
     IF( ASSOCIATED(sp%knots) ) DEALLOCATE(sp%knots)
     ALLOCATE( sp%knots(-p:nints+p) )
     sp%knots(0:nints) = grid(:)
 !
 !  Is the grid equidistant ?
     IF( PRESENT(nlequid) ) THEN
        sp%nlequid = nlequid
     ELSE
        sp%nlequid = is_equid(grid)
     END IF
 !
 !  Coarse to fine mesh mapping for non-equidistant mesh
     IF(sp%nlequid) THEN
        sp%hinv = 1.0d0/(sp%knots(1)-sp%knots(0))
     ELSE
        IF(ASSOCIATED(sp%fmap)) DEALLOCATE(sp%fmap)
        CALL create_fine(sp%knots(0:nints), h, sp%fmap)
        sp%hinv = 1.0d0/h
     END IF
 !
 !  Extend knots at both sides of given grid points
     IF( sp%period ) THEN
        leng = sp%knots(nints) - sp%knots(0)
        DO i=-1,-p,-1
           sp%knots(i) = sp%knots(nints+i) - leng
        END DO
        DO i=1,p
           sp%knots(nints+i) = sp%knots(i) + leng
        END DO
 !!$       sp%knots(-p:-1) = sp%knots(nints-p:nints-1) - leng
 !!$       sp%knots(nints+1:nints+p) = leng + sp%knots(1:p)
     ELSE
        sp%knots(-p:-1) = sp%knots(0)
        sp%knots(nints+1:nints+p) = sp%knots(nints)
     END IF
 !
 !  Precalculated values of all splines and their derivatives at left boundaries 
     IF( ASSOCIATED(sp%val0) ) DEALLOCATE(sp%val0)
     ALLOCATE( sp%val0(0:p, p+1, 1:nints) )
     sp%val0 = 0.0d0
     DO i=1,nints
        xp = sp%knots(i-1) + EPSILON(1.0d0)*ABS(sp%knots(i-1))
        CALL basfun_recur(xp, sp, temp, i)
        sp%val0(:,:,i) = TRANSPOSE(temp)
     END DO
 !
     factinv = 1.0d0
     DO k=2,p   !   Divide by k! for use in PPFORM_ALT
        factinv = factinv/k
        sp%val0(k,:,:) = sp%val0(k,:,:)*factinv
     END DO
 !
 ! Case of periodic equidistant splines (translational invariance)
     IF(sp%period .AND. sp%nlequid) THEN
        IF( ASSOCIATED(sp%valc) ) DEALLOCATE(sp%valc)
        ALLOCATE(sp%valc(0:p, p+1))
        sp%valc = sp%val0(:,:,1)
     END IF
 !
 !  Gauss abscissas and weights
     IF( ngauss .GT. 0 ) THEN
        IF( ASSOCIATED(sp%gausx) ) DEALLOCATE(sp%gausx)
        IF( ASSOCIATED(sp%gausw) ) DEALLOCATE(sp%gausw)
        ALLOCATE(sp%gausx(ngauss,nints))
        ALLOCATE(sp%gausw(ngauss,nints))
        DO i=1,nints
           CALL gauleg(sp%knots(i-1), sp%knots(i),  &
                &      sp%gausx(1:ngauss,i), sp%gausw(1:ngauss,i), ngauss)
        END DO
     END IF
 !
 !  Compute integral of each splines
     IF( ASSOCIATED(sp%intspl) ) DEALLOCATE(sp%intspl)
     ALLOCATE(sp%intspl(0:sp%dim-1))
     CALL calc_integ(sp, sp%intspl)
 !
   END SUBROUTINE set_spline1d
 !===========================================================================
   SUBROUTINE init_dft(sp, kmin, kmax, couplings)
 !
 !   Initialize DFT
 !
     TYPE(spline1d)      :: sp
     INTEGER, INTENT(in) :: kmin, kmax
     INTEGER, INTENT(in), OPTIONAL :: couplings(:)
 !
     INTEGER :: n, p, dk, k, j, nc
     DOUBLE COMPLEX :: zc
 !
     n = sp%nints
     p = sp%order-1
     dk = kmax-kmin+1
 !
 ! Check that -N/2 .LE. Kmin .LE. Kmax .LT. N/2
 !
     IF(kmin.GT.kmax .OR. kmin.LT.-n/2 .OR. kmax.GE.n/2) THEN
        WRITE(*,'(a,2i6,a)') 'kmin, kmax =', kmin, kmax, ' erroneous!'
        STOP
     END IF
 !
 ! The Fourier window
 !
     sp%dft%n = n
     sp%dft%kmin = kmin
     sp%dft%kmax = kmax
     sp%dft%dk = dk
     sp%dft%dx = sp%knots(1) - sp%knots(0)
 !
 ! Precalculate the DFT coefs  exp( i(2*pi/N)jk ), k=kmin,kmax, j=0,p
 !
     IF( ASSOCIATED(sp%dft%coefs) ) DEALLOCATE(sp%dft%coefs)
     ALLOCATE(sp%dft%coefs(kmin:kmax,0:p))
     zc = EXP( CMPLX(0.0d0, 2.0d0*pi/REAL(n,8),8) )
     sp%dft%coefs(:,0) = 1.0d0       ! j=0
     sp%dft%coefs(kmin,1) = zc**kmin ! j=1 
     DO k=kmin+1,kmax
        sp%dft%coefs(k,1) = sp%dft%coefs(k-1,1)*zc
     END DO
     DO j=2,p
        sp%dft%coefs(:,j) = sp%dft%coefs(:,1)*sp%dft%coefs(:,j-1)
     END DO
 !
 !   Mode couplings: by default use the whole window
 !
     nc = dk
     IF( PRESENT(couplings)) nc = SIZE(couplings)
 !
     IF(ASSOCIATED(sp%dft%mode_couplings)) DEALLOCATE(sp%dft%mode_couplings)
     ALLOCATE(sp%dft%mode_couplings(nc))
 !
     IF(PRESENT(couplings)) THEN
        sp%dft%mode_couplings = couplings
     ELSE
        sp%dft%mode_couplings = (/ (k,k=kmin,kmax) /)
     END IF
   END SUBROUTINE init_dft
 !===========================================================================
   SUBROUTINE set_spline2d(p, ngauss, grid1, grid2, sp, period, nlppform,&
        &                  nlequid)
 !
 !  Setup a 2d spline
 !
     INTEGER, INTENT(in) :: p(2), ngauss(2)
     DOUBLE PRECISION, INTENT(in)  :: grid1(:)
     DOUBLE PRECISION, INTENT(in)  :: grid2(:)
     TYPE(spline2d), INTENT(out)   :: sp
     LOGICAL, OPTIONAL, INTENT(in) :: period(2)
     LOGICAL, OPTIONAL, INTENT(in) :: nlppform
     LOGICAL, OPTIONAL, INTENT(in) :: nlequid(2)
 !
     IF(PRESENT(period).AND.PRESENT(nlppform).AND.PRESENT(nlequid)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, period(1), nlppform, &
             &            nlequid(1))
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, period(2), nlppform, &
             &            nlequid(2))
     ELSE IF(PRESENT(period).AND.PRESENT(nlppform)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, period(1), nlppform)
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, period(2), nlppform)
     ELSE IF(PRESENT(period).AND.PRESENT(nlequid)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, period=period(1), nlequid=nlequid(1))
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, period=period(2), nlequid=nlequid(2))
     ELSE IF(PRESENT(nlppform).AND.PRESENT(nlequid)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, nlppform=nlppform, nlequid=nlequid(1))
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, nlppform=nlppform, nlequid=nlequid(2))
     ELSE IF(PRESENT(period)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, period(1))
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, period(2))
     ELSE IF(PRESENT(nlppform)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, nlppform=nlppform)
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, nlppform=nlppform)
     ELSE IF(PRESENT(nlequid)) THEN
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1, nlequid=nlequid(1))
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2, nlequid=nlequid(2))
     ELSE
        CALL set_spline1d(p(1), ngauss(1), grid1, sp%sp1)
        CALL set_spline1d(p(2), ngauss(2), grid2, sp%sp2)
     END IF
   END SUBROUTINE set_spline2d
 !===========================================================================
   SUBROUTINE set_spline2d1d(p, ngauss, grid1, grid2, grid3, sp, period, &
        &                    nlppform, nlequid)
 !
 !  Setup a 2d1d spline (for axisymmetric problems)
 !
     INTEGER, INTENT(in) :: p(3), ngauss(3)
     DOUBLE PRECISION, INTENT(in)  :: grid1(:)
     DOUBLE PRECISION, INTENT(in)  :: grid2(:)
     DOUBLE PRECISION, INTENT(in)  :: grid3(:)
     TYPE(spline2d1d), INTENT(out) :: sp
     LOGICAL, OPTIONAL, INTENT(in) :: period(3)
     LOGICAL, OPTIONAL, INTENT(in) :: nlppform
     LOGICAL, OPTIONAL, INTENT(in) :: nlequid(3)
 !
     IF(PRESENT(period).AND.PRESENT(nlppform).AND.PRESENT(nlequid)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, period(1:2),&
             &            nlppform, nlequid(1:2))
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, period(3), nlppform, &
             &            nlequid(3))
     ELSE IF(PRESENT(period).AND.PRESENT(nlppform)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, period(1:2),&
             &            nlppform)
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, period(3), nlppform)
     ELSE IF(PRESENT(period).AND.PRESENT(nlequid)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, period=period(1:2),&
             &            nlequid=nlequid(1:2))
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, period=period(3), &
             &            nlequid=nlequid(3))
     ELSE IF(PRESENT(nlppform).AND.PRESENT(nlequid)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, nlppform=nlppform,&
             &            nlequid=nlequid(1:2))
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, nlppform=nlppform,  &
             &            nlequid=nlequid(3))
     ELSE IF(PRESENT(period)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, period(1:2))
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, period(3))
     ELSE IF(PRESENT(nlppform)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, nlppform=nlppform)
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, nlppform=nlppform)
     ELSE IF(PRESENT(nlequid)) THEN
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12, nlequid=nlequid(1:2))
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3, nlequid=nlequid(3))
     ELSE
        CALL set_spline2d(p(1:2), ngauss(1:2), grid1, grid2, sp%sp12)
        CALL set_spline1d(p(3), ngauss(3), grid3, sp%sp3)
     END IF
   END SUBROUTINE set_spline2d1d
 !===========================================================================
   SUBROUTINE get_dim1(sp, dim, nx, nidbas)
 !
 !  Return spline dimension of 1d spline sp and optionally
 !  number of knot intervals nx and spline degree nidbas
 !
     TYPE(spline1d), INTENT(in) :: sp
     INTEGER, INTENT(out) :: dim
     INTEGER, OPTIONAL, INTENT(out) :: nx, nidbas
     dim = sp%dim
     IF( PRESENT(nx) ) nx = sp%nints
     IF( PRESENT(nidbas) ) nidbas = sp%order - 1
   END SUBROUTINE get_dim1
 !===========================================================================
   SUBROUTINE get_dim2(sp, dim, nx, nidbas)
 !
 !  Return spline dimension of 2d spline sp and optionally
 !  number of knot intervals nx and spline degree nidbas
 !
     TYPE(spline2d), INTENT(in) :: sp
     INTEGER, INTENT(out) :: dim(2)
     INTEGER, OPTIONAL, INTENT(out) :: nx(2), nidbas(2)
 !
     dim(1) = sp%sp1%dim
     IF( PRESENT(nx) ) nx(1) = sp%sp1%nints
     IF( PRESENT(nidbas) ) nidbas(1) = sp%sp1%order - 1
 !
     dim(2) = sp%sp2%dim
     IF( PRESENT(nx) ) nx(2) = sp%sp2%nints
     IF( PRESENT(nidbas) ) nidbas(2) = sp%sp2%order - 1
   END SUBROUTINE get_dim2
 !===========================================================================
   SUBROUTINE get_gauss(sp, n, i, x, w)
 !
 !   Get Gauss points and weights from spline sp
 !
     TYPE(spline1d), INTENT(in) :: sp
     INTEGER, INTENT(out) :: n
     INTEGER, INTENT(in), OPTIONAL :: i
     DOUBLE PRECISION, DIMENSION(:), OPTIONAL, INTENT(out) :: x, w
 !
     n = SIZE(sp%gausx, 1)
     IF( PRESENT(i) ) THEN 
        x(:) = sp%gausx(:,i)
        w(:) = sp%gausw(:,i)
     END IF
   END SUBROUTINE get_gauss
 !===========================================================================
   SUBROUTINE def_basfun0(xp, sp, fun, left)
 !
 !  Define the basis function and its derivatives at x
 !  fun(i,j) = (j-1)th derivative of ith basis function.
 !
     DOUBLE PRECISION, INTENT(in) :: xp
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(out) :: fun(:,:)
     INTEGER, OPTIONAL, INTENT(out) :: left
     DOUBLE PRECISION :: x
     INTEGER :: p, n, kleft
     INTEGER :: ierr, j, k
 !
     CALL locintv(sp, xp, kleft)
     CALL basfun(xp, sp, fun, kleft+1)
     IF(PRESENT(left)) THEN
        left = kleft
     END IF
   END SUBROUTINE def_basfun0
 !===========================================================================
   SUBROUTINE basfun0(xp, sp, f, left)
 !
 !  Define the basis function and its derivatives at x, in interval
 !  [left,left+1], using PPFORM defined by sp%val0., left=1,..,nints
 !    f(i,j) = jth derivative of ith basis function.
 !
     DOUBLE PRECISION, INTENT(in)   :: xp
+    TYPE(spline1d), INTENT(in)     :: sp
     DOUBLE PRECISION, INTENT(out)  :: f(:,0:)
     INTEGER, INTENT(in)            :: left   ! =1,2,...,nints
-    TYPE(spline1d) :: sp
 !
     INTEGER          :: p, n, jdermx, i, jder
     DOUBLE PRECISION :: x, h
 !
     p = sp%order - 1
     n = sp%nints
     jdermx = SIZE(f,2)-1
 !
     h = xp-sp%knots(left-1)  ! knots are numbered from 0
 !
     IF(sp%period .AND. sp%nlequid) THEN
        DO jder=0,jdermx   ! Derivative jder
           CALL my_ppval(p, h, sp%valc, jder, f(:,jder))
        END DO
     ELSE
        DO jder=0,jdermx   ! Derivative jder
           CALL my_ppval(p, h, sp%val0(:,:,left), jder, f(:,jder))
        END DO
     END IF
   CONTAINS
     SUBROUTINE my_ppval(p, x, ppform, jder, f)
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x, ppform(:,:)
       INTEGER, INTENT(in) :: jder
       DOUBLE PRECISION, INTENT(out) :: f(:)
       DOUBLE PRECISION :: fact
       INTEGER :: j
       SELECT CASE (jder)
       CASE(0)            ! function value
          SELECT CASE(p)
          CASE(1)
             f(1) = ppform(1,1) + x*ppform(2,1)
          CASE(2)
             f(1) = ppform(1,1) + x*(ppform(2,1)+x*ppform(3,1))
             f(2) = ppform(1,2) + x*(ppform(2,2)+x*ppform(3,2))
          CASE(3)
             f(1) = ppform(1,1) + x*(ppform(2,1)+x*(ppform(3,1)+x*ppform(4,1))) 
             f(2) = ppform(1,2) + x*(ppform(2,2)+x*(ppform(3,2)+x*ppform(4,2)))
             f(3) = ppform(1,3) + x*(ppform(2,3)+x*(ppform(3,3)+x*ppform(4,3)))
         CASE(4:)
             f(1:p) = ppform(p+1,1:p)
             DO j=p,1,-1
                f(1:p) = f(1:p)*x + ppform(j,1:p)
             END DO
          END SELECT
          f(p+1) = 1.0d0 - SUM(f(1:p))
       CASE(1)            ! 1st derivative
          SELECT CASE(p)
          CASE(1)
             f(1) = ppform(2,1)
          CASE(2)
             f(1) = ppform(2,1) + x*2.d0*ppform(3,1)
             f(2) = ppform(2,2) + x*2.d0*ppform(3,2)
          CASE(3)
             f(1) = ppform(2,1) + x*(2.d0*ppform(3,1)+x*3.0d0*ppform(4,1))
             f(2) = ppform(2,2) + x*(2.d0*ppform(3,2)+x*3.0d0*ppform(4,2))
             f(3) = ppform(2,3) + x*(2.d0*ppform(3,3)+x*3.0d0*ppform(4,3))
          CASE(4:)
             f(1:p) = p*ppform(p+1,1:p)
             DO j=p-1,1,-1
                f(1:p) = f(1:p)*x + j*ppform(j+1,1:p)
             END DO
          END SELECT
          f(p+1) = -SUM(f(1:p))
       CASE default       ! 2nd and higher derivatives
          fact = p-jder
          f(1:p) = ppform(p+1,1:p)
          DO j=p,jder+1,-1
             f(1:p) = f(1:p)/fact*j*x + ppform(j,1:p)
             fact = fact-1.0d0
          END DO
          DO j=2,jder
             f(1:p) = f(1:p)*j
          END DO
          f(p+1) = -SUM(f(1:p))
       END SELECT
     END SUBROUTINE my_ppval
   END SUBROUTINE basfun0
 !===========================================================================
   SUBROUTINE basfun1(xp, sp, f, left)
 !
 !  Define the basis function and its derivatives at x, in interval i=1,2,
 !  using PPFORM defined by sp%val0.
 !    f(i,j,p) = jth derivative of ith basis function at coordinate xp
 !
-    DOUBLE precision, INTENT(in)   :: xp(:)
+    DOUBLE PRECISION, INTENT(in)   :: xp(:)
+    TYPE(spline1d), INTENT(in)     :: sp
     DOUBLE PRECISION, INTENT(out)  :: f(0:,0:,:)
     INTEGER, INTENT(in)            :: left(:)  ! =1,2,...,nints
-    TYPE(spline1d) :: sp
 !
     INTEGER          :: p, n, kleft, i, j, jder, ierr
     INTEGER :: npt, jdermx
     DOUBLE PRECISION :: h(SIZE(xp)), temp(SIZE(xp))
     DOUBLE PRECISION :: ppform(SIZE(xp),sp%order)
 !
     p = sp%order - 1
     n = sp%nints
     npt = SIZE(xp)
     jdermx = SIZE(f,2)-1
 !
     h = xp - sp%knots(left-1)  ! knots are numbered from 0
 !
     IF( sp%period .AND. sp%nlequid) THEN
        DO jder=0,jdermx
           CALL my_ppval_same(p, h, sp%valc, jder, f(:,jder,1:npt))
        END DO
     ELSE
        DO i=0,p                  ! Spline i
           DO j=1,npt
              ppform(j,:) = sp%val0(:,i+1,left(j))
           END DO
           DO jder=0,jdermx       ! Derivative jder
              CALL my_ppval(p, h, ppform, jder, temp)
              f(i,jder,1:npt) = temp
           END DO
        END DO
     END IF
   CONTAINS
 !+++
     SUBROUTINE my_ppval(p, x, ppform, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !   for many points x(:)
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x(:)
       DOUBLE PRECISION, INTENT(in) :: ppform(:,:)
       INTEGER, INTENT(in) :: jder
       DOUBLE PRECISION, INTENT(out) :: f(:)
       DOUBLE PRECISION :: fact
       INTEGER :: j
       SELECT CASE (jder)
       CASE(0)            ! function value
          SELECT CASE(p)
          CASE(1)
             f(:) = ppform(:,1) + x(:)*ppform(:,2)
          CASE(2)
             f(:) = ppform(:,1) + x(:)*(ppform(:,2)+x(:)*ppform(:,3))
          CASE(3)
             f(:) = ppform(:,1) + x(:)*(ppform(:,2)+x(:)*(ppform(:,3)+x(:)*ppform(:,4)))
          CASE(4:)
             f(:) = ppform(:,p+1)
             DO j=p,1,-1
                f(:) = ppform(:,j) + f(:)*x(:)
             END DO
          END SELECT
       CASE(1)            ! 1st derivative
          SELECT CASE(p)
          CASE(1)
             f(:) = ppform(:,2)
          CASE(2)
             f(:) = ppform(:,2) + x(:)*2.d0*ppform(:,3)
          CASE(3)
             f(:) = ppform(:,2) + x(:)*(2.d0*ppform(:,3)+x(:)*3.0d0*ppform(:,4))
          CASE(4:)
             f(:) = p*ppform(:,p+1)
             DO j=p-1,1,-1
                f(:) = f(:)*x(:) + j*ppform(:,j+1)
             END DO
          END SELECT
       CASE default       ! 2nd and higher derivatives
          f(:) = ppform(:,p+1)
          fact = p-jder
          DO j=p,jder+1,-1
             f(:) = f(:)/fact*j*x(:) + ppform(:,j)
             fact = fact-1.0d0
          END DO
          DO j=2,jder
             f(:) = f(:)*j
          END DO
       END SELECT
     END SUBROUTINE my_ppval
 !+++
     SUBROUTINE my_ppval_same(p, x, ppform, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !   for many points x(:), same ppform (translationnal invariant spline)
     INTEGER, INTENT(in) :: p
     DOUBLE PRECISION, INTENT(in) :: x(:)
     DOUBLE PRECISION, INTENT(in) :: ppform(:,:)
     INTEGER, INTENT(in) :: jder
     DOUBLE PRECISION, INTENT(out) :: f(:,:)
     DOUBLE PRECISION :: fact
     INTEGER :: j,k
     SELECT CASE (jder)
 !
 ! function value
     CASE(0)            
        SELECT CASE(p)
        CASE(1)
           f(1,:) = ppform(1,1) + x(:)*ppform(2,1)
        CASE(2)
           f(1,:) = ppform(1,1) + x(:)*(ppform(2,1)+x(:)*ppform(3,1))
           f(2,:) = ppform(1,2) + x(:)*(ppform(2,2)+x(:)*ppform(3,2))
        CASE(3)
           f(1,:) = ppform(1,1) + x(:)*(ppform(2,1)+x(:)*(ppform(3,1)+x(:)*ppform(4,1)))
           f(2,:) = ppform(1,2) + x(:)*(ppform(2,2)+x(:)*(ppform(3,2)+x(:)*ppform(4,2)))
           f(3,:) = ppform(1,3) + x(:)*(ppform(2,3)+x(:)*(ppform(3,3)+x(:)*ppform(4,3)))
        CASE(4:)
           DO k=1,p
              f(k,:) = ppform(p+1,k)
              DO j=p,1,-1
                 f(k,:) = ppform(j,k) + f(k,:)*x(:)
              END DO
           END DO
        END SELECT
        f(p+1,:) = 1.0d0 - SUM(f(1:p,:),DIM=1)
 !
 ! 1st derivative
     CASE(1)         
        SELECT CASE(p)
        CASE(1)
           f(1,:) = ppform(2,1)
        CASE(2)
           f(1,:) = ppform(2,1) + x(:)*2.d0*ppform(3,1)
           f(2,:) = ppform(2,2) + x(:)*2.d0*ppform(3,2)
        CASE(3)
           f(1,:) = ppform(2,1) + x(:)*(2.d0*ppform(3,1)+x(:)*3.0d0*ppform(4,1))
           f(2,:) = ppform(2,2) + x(:)*(2.d0*ppform(3,2)+x(:)*3.0d0*ppform(4,2))
           f(3,:) = ppform(2,3) + x(:)*(2.d0*ppform(3,3)+x(:)*3.0d0*ppform(4,3))
        CASE(4:)
           DO k=1,p
              f(k,:) = p*ppform(p+1,k)
              DO j=p-1,1,-1
                 f(k,:) = f(k,:)*x(:) + j*ppform(j+1,k)
              END DO
           END DO
        END SELECT
        f(p+1,:) = -SUM(f(1:p,:),DIM=1)
 !
 ! 2nd and higher derivatives
        CASE(2:)        
           DO k=1,p
              f(k,:) = ppform(p+1,k)
              fact = p-jder
              DO j=p,jder+1,-1
                 f(k,:) = f(k,:)/fact*j*x(:) + ppform(j,k)
                 fact = fact-1.0d0
              END DO
              DO j=2,jder
                 f(k,:) = f(k,:)*j
              END DO
           END DO
           f(p+1,:) = -SUM(f(1:p,:),DIM=1)
        END SELECT
      END SUBROUTINE my_ppval_same
 !+++
   END SUBROUTINE basfun1
 !===========================================================================
   SUBROUTINE def_basfun1(xp, sp, fun, left)
 !
 !  Define the basis function and its derivatives at x
 !  fun(i,j) = (j-1)th derivative of ith basis function.
 !
     DOUBLE PRECISION, INTENT(in) :: xp(:)
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(out) :: fun(:,:,:)
     INTEGER, OPTIONAL, INTENT(out) :: left(:)
     DOUBLE PRECISION :: x(SIZE(xp))
     INTEGER :: kleft(SIZE(xp))
     INTEGER :: p, n
     INTEGER :: ierr, j, k
 !
     CALL locintv(sp, xp, kleft)
     CALL basfun(xp, sp, fun, kleft+1)
     IF(PRESENT(left)) THEN
        left = kleft
     END IF
   END SUBROUTINE def_basfun1
 !===========================================================================
   SUBROUTINE ft_basfun0(xp, sp, ft_f, left)
 !
 !  DFT of basis functions: ft_f(k,j), k=sp%dft%kmin, sp$dft%kmax (modes)
 !                                     j=0, p-1 (order of derivative))
 !
     DOUBLE PRECISION, INTENT(in)   :: xp
     DOUBLE COMPLEX, INTENT(out)    :: ft_f(:,:)
     INTEGER, INTENT(in)            :: left
     TYPE(spline1d)                 :: sp
     DOUBLE PRECISION :: f(sp%order,SIZE(ft_f,2))
 !
 !  Construct all splines on interval [left,left+1] at coordinate  xp
     CALL basfun(xp, sp, f, left)
 !
 !  DFT of splines
     ft_f = MATMUL(sp%dft%coefs, f)    
   END SUBROUTINE ft_basfun0
 !===========================================================================
   SUBROUTINE ft_basfun1(xp, sp, ft_f, left)
 !
 !  DFT of basis functions: ft_f(k,j), k=sp%dft%kmin, sp$dft%kmax (modes)
 !                                     j=0, p-1 (order of derivative))
 !                          at xp(i)
 !
     DOUBLE PRECISION, INTENT(in)   :: xp(:)
     DOUBLE COMPLEX, INTENT(out)    :: ft_f(:,:,:)
     INTEGER, INTENT(in)            :: left(:)
     TYPE(spline1d)                 :: sp
 !
     INTEGER :: i, n3
     DOUBLE PRECISION :: f(sp%order,SIZE(ft_f,2),SIZE(ft_f,3))
 !
 !  Construct all splines on interval [left,left+1] at coordinate  xp
     CALL basfun(xp, sp, f, left)
 !
 !  DFT of splines
     n3 = SIZE(xp)
     DO i=1,n3
        ft_f(:,:,i) = MATMUL(sp%dft%coefs, f(:,:,i))
     END DO
   END SUBROUTINE ft_basfun1
 !===========================================================================
   SUBROUTINE basfun_recur(xp, sp, fun, left)
 !
 !  Define the basis function and its derivatives at x, in interval i=1,2,
 !  using recurrence construct in function BVALUE
 !  fun(i,j) = (j-1)th derivative of ith basis function.
 !
     DOUBLE PRECISION, INTENT(in) :: xp
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(out) :: fun(:,:)
     INTEGER, INTENT(in) :: left
     DOUBLE PRECISION :: bcoef(1)=1.0d0, bvalue, x
     INTEGER :: p, n, kleft
     INTEGER :: ierr, j, k
 !
     p = sp%order - 1
     n = sp%nints
     fun = 0.0d0
 !
     IF( sp%period ) THEN   !  ** Applly periodicity **
        x =sp%knots(0) + MODULO(xp-sp%knots(0), sp%lperiod)
     ELSE
        x = xp
     END IF
 !
     kleft = left-1
     DO j=kleft-p, kleft
        DO k=0, SIZE(fun,2)-1
           fun(j-kleft+p+1, k+1) = bvalue(sp%knots(j), bcoef, 1, p+1, x, k) 
        END DO
     END DO
   END SUBROUTINE basfun_recur
 !===========================================================================
   SUBROUTINE gauleg(x1,x2,x,w,n)
 !
 !   Compute Gauss-Legendre abscissas and weights in interval [x1, x2]
 !
     INTEGER, INTENT(in) :: n
     DOUBLE PRECISION, INTENT(in) :: x1,x2
     DOUBLE PRECISION, INTENT(out) :: x(n),w(n)
     DOUBLE PRECISION :: EPS
     INTEGER i,j,m
     DOUBLE PRECISION p1,p2,p3,pp,xl,xm,z,z1
 !
     eps=EPSILON(eps)
     m=(n+1)/2
     xm=0.5d0*(x2+x1)
     xl=0.5d0*(x2-x1)
     DO i=1,m
        z=COS(3.141592654d0*(i-.25d0)/(n+.5d0))
        DO
           p1=1.d0; p2=0.d0
           DO j=1,n
              p3=p2; p2=p1
              p1=((2.d0*j-1.d0)*z*p2-(j-1.d0)*p3)/j
           END DO
           pp=n*(z*p1-p2)/(z*z-1.d0)
           z1=z
           z=z1-p1/pp
           IF( ABS(z-z1) .LE. EPS ) EXIT
        END DO
        x(i)=xm-xl*z
        x(n+1-i)=xm+xl*z
        w(i)=2.d0*xl/((1.d0-z*z)*pp*pp)
        w(n+1-i)=w(i)
     END DO
   END SUBROUTINE gauleg
 !===========================================================================
   SUBROUTINE gridval1dz(sp, xp, f, jder, c, ppformz)
 !
 !  Compute values or jder-th dervivative of f(x) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients are given.
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp
     DOUBLE COMPLEX, DIMENSION(:), INTENT(out) :: f
     INTEGER, INTENT(in) :: jder
     DOUBLE COMPLEX, DIMENSION(:), OPTIONAL, INTENT(in) :: c
     DOUBLE COMPLEX, DIMENSION(:,:), OPTIONAL :: ppformz
     DOUBLE PRECISION, ALLOCATABLE :: fun(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), h, fact
     INTEGER :: order, nints, i, j, nidbas
     INTEGER :: leftx(SIZE(xp))
 !
     order = sp%order
     nints = sp%nints
     nidbas = order-1
 !
 !   Compute PPFORM/BCOEFS if spline coefs are passed
 !
     IF (PRESENT(c)) THEN
        IF (sp%nlppform) THEN
           IF( PRESENT(ppformz) ) THEN
              CALL topp0z(sp, c, ppformz)
           ELSE
              IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE(sp%ppformz)
              ALLOCATE(sp%ppformz(order,nints))
              CALL topp0z(sp, c, sp%ppformz)
           END IF
        ELSE
           IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
           ALLOCATE(sp%bcoefsc(SIZE(c)))
           sp%bcoefsc = c
        END IF
     END IF
 !
 !  Applly periodicity if required
 !
     IF( sp%period ) THEN
        x =sp%knots(0) + MODULO(xp-sp%knots(0), sp%lperiod)
     ELSE
        x = xp
     END IF
 !
 !  Locate the intervals containing x
 !
     CALL locintv(sp, x, leftx)
 !
 !  Compute function/derivatives
 !
     IF( sp%nlppform ) THEN   ! using PP form
        DO i=1,SIZE(x)
           IF( PRESENT(ppformz) ) THEN
              CALL ppval(sp, x(i), ppformz(:,leftx(i)+1), leftx(i), jder, f(i))
           ELSE
              CALL ppval(sp, x(i), sp%ppformz(:,leftx(i)+1), leftx(i), jder, f(i))
           END IF
        END DO
     ELSE                     ! using spline expansion
        ALLOCATE(fun(0:nidbas,0:jder))
        f = 0.0d0
        DO i=1,SIZE(x)
           CALL basfun(x(i), sp, fun, leftx(i)+1)
           DO j=0,nidbas
              f(i) = f(i) + sp%bcoefsc(leftx(i)+j+1)*fun(j,jder)
           END DO
        END DO
        DEALLOCATE(fun)
     END IF
 !
   END SUBROUTINE gridval1dz
 !===========================================================================
   SUBROUTINE gridval1d(sp, xp, f, jder, c, ppform)
 !
 !  Compute values or jder-th dervivative of f(x) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients are given.
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp
     DOUBLE PRECISION, DIMENSION(:), INTENT(out) :: f
     INTEGER, INTENT(in) :: jder
     DOUBLE PRECISION, DIMENSION(:), OPTIONAL, INTENT(in) :: c
     DOUBLE PRECISION, DIMENSION(:,:), OPTIONAL :: ppform
     DOUBLE PRECISION, ALLOCATABLE :: fun(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), h, fact
     INTEGER :: order, nints, i, j, nidbas
     INTEGER :: leftx(SIZE(xp))
 !
     order = sp%order
     nints = sp%nints
     nidbas = order-1
 !
 !   Compute PPFORM/BCOEFS if spline coefs are passed
 !
     IF (PRESENT(c)) THEN
        IF (sp%nlppform) THEN
           IF( PRESENT(ppform) ) THEN
              CALL topp0(sp, c, ppform)
           ELSE
              IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
              ALLOCATE(sp%ppform(order,nints))
              CALL topp0(sp, c, sp%ppform)
           END IF
        ELSE
           IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
           ALLOCATE(sp%bcoefs(SIZE(c)))
           sp%bcoefs = c
        END IF
     END IF
 !
 !  Applly periodicity if required
 !
     IF( sp%period ) THEN
        x =sp%knots(0) + MODULO(xp-sp%knots(0), sp%lperiod)
     ELSE
        x = xp
     END IF
 !
 !  Locate the intervals containing x
 !
     CALL locintv(sp, x, leftx)
 !
 !  Compute function/derivatives
 !
     IF( sp%nlppform ) THEN   ! using PP form
        DO i=1,SIZE(x)
           IF( PRESENT(ppform) ) THEN
              CALL ppval(sp, x(i), ppform(:,leftx(i)+1), leftx(i), jder, f(i))
           ELSE
              CALL ppval(sp, x(i), sp%ppform(:,leftx(i)+1), leftx(i), jder, f(i))
           END IF
        END DO
     ELSE                     ! using spline expansion
        ALLOCATE(fun(0:nidbas,0:jder))
        f = 0.0d0
        DO i=1,SIZE(x)
           CALL basfun(x(i), sp, fun, leftx(i)+1)
           DO j=0,nidbas
              f(i) = f(i) + sp%bcoefs(leftx(i)+j+1)*fun(j,jder)
           END DO
        END DO
        DEALLOCATE(fun)
     END IF
 !
   END SUBROUTINE gridval1d
 !===========================================================================
   SUBROUTINE def_knots(p, xg, knots, period, nlskip)
 !
 !   Define spline knots for interpolating at sites given by xg
 !
     INTEGER, INTENT(in) :: p
     DOUBLE PRECISION, INTENT(in) :: xg(0:)
     DOUBLE PRECISION, POINTER :: knots(:)
     LOGICAL, OPTIONAL, INTENT(in) :: period, nlskip
     LOGICAL :: kperiod, mlskip
     INTEGER :: npt, dim, nx, i, ii
 !
     kperiod=.FALSE.
     mlskip = .TRUE.
     IF( PRESENT(period) ) kperiod=period
     IF( PRESENT(nlskip) ) mlskip = nlskip
 !
 !    Periodic splines
 !
     IF( kperiod ) THEN
        nx = SIZE(xg) -1
        IF( ASSOCIATED(knots) ) DEALLOCATE(knots)
        ALLOCATE(knots(0:nx))
        IF( MODULO(p,2) .NE. 0 ) THEN  ! Odd degree
           knots(0:nx) = xg(0:nx)
        ELSE                           ! Even degree
           DO i=1,nx
              knots(i) = 0.5d0*(xg(i-1)+xg(i))
           END DO
           knots(0) = knots(nx) - (xg(nx)-xg(0))
        END IF
        RETURN
     END IF
 !
 !    Non-periodic splines
 !
     npt = SIZE(xg)
     dim = npt
     IF( .NOT. mlskip ) THEN
        dim = dim + 2*(p/2) ! Add BC on derivatives
     END IF
     nx = dim-p
     IF( ASSOCIATED(knots) ) DEALLOCATE(knots)
     ALLOCATE(knots(0:nx))
 !
     knots(0) = xg(0)
     knots(nx) = xg(npt-1)
 !
     IF( MODULO(p,2) .EQ. 0 ) THEN
        ii = 0
        IF( mlskip ) ii = p/2     ! skip first p/2 intervals
        DO i=1,nx-1
           ii = ii+1
           knots(i) = (xg(ii)+xg(ii-1))/2
        END DO
     ELSE
        ii = 0
        IF( mlskip ) ii = (p-1)/2  ! skip (p-1)/2 points after the first point ii=0
        DO i=1,nx-1
           ii = ii+1
           knots(i) = xg(ii)
        END DO
     END IF
 !
   END SUBROUTINE def_knots
 !===========================================================================
   SUBROUTINE allsplines(sp, xpt, splines)
 !
 !   Return all splines defined on points xpt
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: xpt(:)
     DOUBLE PRECISION, POINTER :: splines(:,:)
     DOUBLE PRECISION, ALLOCATABLE :: fun(:,:)
     INTEGER :: i, n, left, dim, p
 !
     p = sp%order - 1
     dim = sp%dim
     n = SIZE(xpt)
 !
     IF( ASSOCIATED(splines) ) DEALLOCATE(splines)
     ALLOCATE(splines(n,dim), fun(p+1,1))
     splines = 0.0d0
     DO i=1,n
        CALL locintv(sp, xpt(i), left)
        CALL basfun(xpt(i), sp, fun, left+1)
        splines(i,left+1:left+p+1) = fun(1:p+1,1)
     END DO
     DEALLOCATE(fun)
   END SUBROUTINE allsplines
 !===========================================================================
   SUBROUTINE set_splcoef1d(p, x, sp, period, ibc)
 !
 !   Setup 1d interpolation matrix for spline of degree p
 !
     INTEGER, INTENT(in) :: p
     DOUBLE PRECISION, INTENT(in) :: x(:)
     TYPE(spline1d), INTENT(out) :: sp
     LOGICAL, OPTIONAL, INTENT(in) :: period
     INTEGER, OPTIONAL :: ibc(:,:)
 !
     LOGICAL :: kperiod
 !
     kperiod = .FALSE.
     IF( PRESENT(period) )  kperiod = period
 !
     IF( kperiod ) THEN
        CALL splcoefp_setup(p, x, sp)
     ELSE
        IF( PRESENT(ibc) ) THEN 
           CALL splcoef_setup(p, x, sp, ibc)
        ELSE
           CALL splcoef_setup(p, x, sp)
        END IF
     END IF
   END SUBROUTINE set_splcoef1d
 !===========================================================================
   SUBROUTINE set_splcoef2d(p, x1, x2, sp, period, ibc1, ibc2)
 !
 !   Setup 2d interpolation matrix for spline of degree p
 !
     INTEGER, INTENT(in) :: p(2)
     DOUBLE PRECISION, INTENT(in) :: x1(:), x2(:)
     TYPE(spline2d), INTENT(out) :: sp
     LOGICAL, OPTIONAL, INTENT(in) :: period(2)
     INTEGER, OPTIONAL :: ibc1(:,:),ibc2(:,:) 
 !
     LOGICAL :: kperiod(2)
 !
     kperiod = .FALSE.
     IF( PRESENT(period) )  kperiod = period
 !
 !  Direction 1
     IF( kperiod(1) ) THEN
        CALL splcoefp_setup(p(1), x1, sp%sp1)
     ELSE
        IF( PRESENT(ibc1) ) THEN 
           CALL splcoef_setup(p(1), x1, sp%sp1, ibc1)
        ELSE
           CALL splcoef_setup(p(1), x1, sp%sp1)
        END IF
     END IF
 !
 !  Direction 2
     IF( kperiod(2) ) THEN
        CALL splcoefp_setup(p(2), x2, sp%sp2)
     ELSE
        IF( PRESENT(ibc2) ) THEN 
           CALL splcoef_setup(p(2), x2, sp%sp2, ibc2)
        ELSE
           CALL splcoef_setup(p(2), x2, sp%sp2)
        END IF
     END IF
   END SUBROUTINE set_splcoef2d
 !===========================================================================
   SUBROUTINE get_splcoef1(sp, f, c, fbc)
 !
 !   Compute the spline coefficients c from grid values f
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: f(:)
     DOUBLE PRECISION, INTENT(out) :: c(:)
     DOUBLE PRECISION, INTENT(in), OPTIONAL :: fbc(:,:)
 !
     IF( sp%period ) THEN
        CALL splcoefp1(sp, f, c)
     ELSE
        IF( PRESENT(fbc) ) THEN
           CALL splcoef1(sp, f, c, fbc)
        ELSE
           CALL splcoef1(sp, f, c)
        END IF
     END IF
   END SUBROUTINE get_splcoef1
 !===========================================================================
   SUBROUTINE get_splcoef2d(sp, f, c, fbc1, fbc2)
 !
 !   Compute the spline coefficients c from 2d grid values f
 !
     TYPE(spline2d) :: sp
     DOUBLE PRECISION, INTENT(in) :: f(:,:)
     DOUBLE PRECISION, INTENT(out) :: c(:,:)
     DOUBLE PRECISION :: ctr(SIZE(c,2), SIZE(f,1))
     DOUBLE PRECISION, INTENT(in), OPTIONAL :: fbc1(:,:,:), fbc2(:,:,:)
 
     DOUBLE PRECISION, DIMENSION(:, :)   , ALLOCATABLE :: c_fbc1_left, c_fbc1_right
     DOUBLE PRECISION, DIMENSION(:, :, :), ALLOCATABLE :: c_fbc1_all
 !
 !  Along direction 2
 !
     IF( PRESENT(fbc2) ) THEN
        CALL get_splcoefn(sp%sp2, TRANSPOSE(f), ctr, fbc2)
     ELSE
        CALL get_splcoefn(sp%sp2, TRANSPOSE(f), ctr)
     END IF
 !
 !  Along direction 1
 !
     IF( PRESENT(fbc1) ) THEN
        ALLOCATE( c_fbc1_left(SIZE(c, 2), SIZE(fbc1, 2)))
        ALLOCATE(c_fbc1_right(SIZE(c, 2), SIZE(fbc1, 2)))
        ALLOCATE(c_fbc1_all(2, SIZE(fbc1, 2), SIZE(c, 2)))
 
        CALL get_splcoefn(sp%sp2, TRANSPOSE(fbc1(1, :, :)), c_fbc1_left )
        CALL get_splcoefn(sp%sp2, TRANSPOSE(fbc1(2, :, :)), c_fbc1_right)
 
        c_fbc1_all(1, :, :) = TRANSPOSE(c_fbc1_left ) 
        c_fbc1_all(2, :, :) = TRANSPOSE(c_fbc1_right) 
 
        CALL get_splcoefn(sp%sp1, TRANSPOSE(ctr), c, c_fbc1_all)
 
        DEALLOCATE(c_fbc1_left, c_fbc1_right, c_fbc1_all)
     ELSE
        CALL get_splcoefn(sp%sp1, TRANSPOSE(ctr), c)
     END IF
 !
   END SUBROUTINE get_splcoef2d
 !===========================================================================
   SUBROUTINE get_splcoef2dz(sp, f, c)
 !
 !   Compute the spline coefficients c from 2d grid values f
 !
     TYPE(spline2d) :: sp
     DOUBLE COMPLEX, INTENT(in) :: f(:,:)
     DOUBLE COMPLEX, INTENT(out) :: c(:,:)
     DOUBLE PRECISION, DIMENSION(SIZE(c,1), SIZE(c,2),2) :: pc
 !
     CALL get_splcoef2d(sp, REAL(f), pc(:,:,1))
     CALL get_splcoef2d(sp, AIMAG(f), pc(:,:,2))
     c(:,:) = CMPLX(pc(:,:,1),pc(:,:,2))
   END SUBROUTINE get_splcoef2dz
 !===========================================================================
   SUBROUTINE get_splcoef1z(sp, f, c, fbc)
 !
 !   Compute the spline coefficients c from grid values f
 !
     TYPE(spline1d) :: sp
     DOUBLE COMPLEX, INTENT(in) :: f(:)
     DOUBLE COMPLEX, INTENT(out) :: c(:)
     DOUBLE COMPLEX, INTENT(in), OPTIONAL :: fbc(:,:)
     DOUBLE PRECISION :: pf(SIZE(f),2), pc(SIZE(c),2)
     DOUBLE PRECISION, ALLOCATABLE ::  pfbc(:,:,:)
 !
     pf(:,1) = REAL(f(:))
     pf(:,2) = AIMAG(f(:))
     IF(PRESENT(fbc)) THEN
        ALLOCATE(pfbc(SIZE(fbc,1),SIZE(fbc,2),2))
        pfbc(:,:,1) = REAL(fbc(:,:))
        pfbc(:,:,2) = AIMAG(fbc(:,:))
        CALL get_splcoefn(sp, pf, pc, pfbc)
        DEALLOCATE(pfbc)
     ELSE
        CALL get_splcoefn(sp, pf, pc)       
     END IF
     c(:) = CMPLX(pc(:,1), pc(:,2))
 !
   END SUBROUTINE get_splcoef1z
 !===========================================================================
   SUBROUTINE get_splcoefn(sp, f, c, fbc)
 !
 !   Compute the spline coefficients c from grid values f
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: f(:,:)
     DOUBLE PRECISION, INTENT(out) :: c(:,:)
     DOUBLE PRECISION, INTENT(in), OPTIONAL :: fbc(:,:,:)
 !
     IF( sp%period ) THEN
        CALL splcoefpn(sp, f, c)
     ELSE
        IF( PRESENT(fbc) ) THEN
           CALL splcoefn(sp, f, c, fbc)
        ELSE
           CALL splcoefn(sp, f, c)
        END IF
     END IF
   END SUBROUTINE get_splcoefn
 !===========================================================================
    SUBROUTINE splcoef_setup(p, x, sp, ibc)
 !
 !   Setup the interpolation matrix
 !   for spline of degree p
 !
     INTEGER, INTENT(in) :: p
     DOUBLE PRECISION, INTENT(in) :: x(:)
     TYPE(spline1d), INTENT(out) :: sp
     INTEGER, OPTIONAL :: ibc(:,:)
     DOUBLE PRECISION, POINTER  :: knots(:)=>NULL(), arow(:)=>NULL(), &
          &                        fun(:,:)=>NULL()
     INTEGER :: nx, dim, kl, ku, rank
     INTEGER :: i, left, ishift
     LOGICAL :: nlskip
 !
 !    Type of Boundary Conditions
     nlskip = .TRUE.
     ishift = 0
     IF( PRESENT(ibc) ) THEN
        nlskip = .FALSE.
        ishift = p/2
     END IF
 !
 !    Set up spline
     nx = SIZE(x) - 1   ! X is the interpolation sites
     CALL def_knots(p, x, knots, nlskip=nlskip)
     CALL set_spline(p, 0, knots, sp)
     sp%nsites = nx + 1 ! Store away the number of interpolation sites
     DEALLOCATE(knots)
 !
 !    Set up interpolation matrix
     dim = sp%dim
     kl = MAX(p-1,0)
     ku = MAX(p-1,0)
     rank = dim
     CALL init(kl, ku, rank, 0, sp%mat)
 !!$    WRITE(*,'(a,3i6)') 'Interpolation matrix:, kl, ku, rank ', kl, ku, rank
 !
 !    COMPUTE matrix row by row
     ALLOCATE(arow(dim), fun(p+1,0:p))
     DO i=1,SIZE(x)
        arow = 0.0d0
        CALL locintv(sp, x(i), left)
        CALL basfun(x(i), sp, fun(:,0:0), left+1)
        arow(left+1:left+p+1) = fun(1:p+1,0)
        CALL putrow(sp%mat, i+ishift, arow)
 !!$       WRITE(*,'(i5,13f8.3)') i+ishift, arow
     END DO
 !
 !    Add BC if specified
     IF( PRESENT(ibc) ) THEN
        CALL locintv(sp, x(1), left)
        CALL basfun(x(1), sp, fun, left+1)      ! BC at the left side
        DO i=1,p/2
           arow = 0.0d0
           arow(left+1:left+p+1) = fun(1:p+1,ibc(1,i))
           CALL putrow(sp%mat, i, arow)
 !!$          WRITE(*,'(i5,13f8.3)') i, arow
        END DO
        CALL locintv(sp, x(SIZE(x)), left)
        CALL basfun(x(SIZE(x)), sp, fun, left+1) ! BC at the right side
        DO i=1,p/2
           arow = 0.0d0
           arow(left+1:left+p+1) = fun(1:p+1,ibc(2,i))
           CALL putrow(sp%mat, dim-i+1, arow)
 !!$          WRITE(*,'(i5,13f8.3)') dim-i+1, arow
        END DO
     END IF
     DEALLOCATE(arow, fun)
 !
 !    Factor the matrix
     CALL factor(sp%mat)
 !
   END SUBROUTINE splcoef_setup
 !===========================================================================
   SUBROUTINE splcoefp_setup(p, x, sp)
 !
 !   Set up the interpolation matrix
 !   for periodic case
 !
     USE matrix
     INTEGER, INTENT(in) :: p
     DOUBLE PRECISION, INTENT(in) :: x(:)
     TYPE(spline1d), INTENT(out) :: sp
 !
     TYPE(gemat) :: hmat
     DOUBLE PRECISION, POINTER :: knots(:)=>NULL(), arow(:)=>NULL(), &
          &                       fun(:,:)=>NULL()
 !!$    DOUBLE PRECISION, POINTER :: arr2d(:,:)=>null()
     INTEGER :: nx, kl, ku, rank, mr, nc
     INTEGER :: i, left, j, jj
     DOUBLE PRECISION, PARAMETER :: zero=0.0d0, one=1.0d0
 !________________________________________________________________________________
 !
 !    Set up spline
 !
     nx = SIZE(x) - 1   ! X is the interpolation sites
     CALL def_knots(p, x, knots, period=.TRUE.)
     CALL set_spline(p, 0, knots, sp, period=.TRUE.)
     sp%nsites = nx + 1 ! Store away the number of interpolation sites
     DEALLOCATE(knots)
 !________________________________________________________________________________
 !
 !    Set up interpolation matrix sp%matp
 !
     kl = MAX(p/2,0)
     ku = kl
     rank = nx
 !!$    WRITE(*,'(a,3i6)') 'Interpolation matrix:, kl, ku, rank ', kl, ku, rank
 !
     CALL init(kl, ku, rank, 0, sp%matp%mat)    ! matp%mat is a GB matrix
     ALLOCATE(sp%matp%matu(rank, kl+ku), sp%matp%matvt(kl+ku,rank))
 !
     sp%matp%matu = zero
     sp%matp%matvt = zero             !    kl = ku = 2
     DO j=1,kl                        !  [ 1 0 0 . . . . ]
        sp%matp%matu(j,j) = one       !  [ 0 1 0 . . . . ]
     END DO                           !  [ . 0 . . . . . ]
     DO j=1,ku                        !  [ . . . . . 0 . ]
        i=rank-ku+j                   !  [ . . . . 0 1 0 ]
        sp%matp%matu(i,kl+j) = one    !  [ . . . . 0 0 1 ] 
     END DO
 !________________________________________________________________________________
 !
 !    COMPUTE matrix row by row
 !
     ALLOCATE(arow(rank), fun(p+1,1))
     DO i=1,rank
        arow = zero
        CALL locintv(sp, x(i), left)
        CALL basfun(x(i), sp, fun, left+1)
        left = left-p/2
        DO j=0,p
           jj = MODULO(left+j, rank) + 1
           arow(jj) = fun(j+1,1)
        END DO
        CALL putrow(sp%matp%mat, i, arow)
        IF( i .LE. kl ) THEN 
           sp%matp%matvt(i,rank-kl+1:rank) = arow(rank-kl+1:rank)
        ELSE IF ( i .GE. rank-ku+1 ) THEN
           j = i-(rank-ku+1) + 1
           sp%matp%matvt(kl+j,1:ku) = arow(1:ku)
        END IF
 !!$       WRITE(8, '(i5, 12(1pe12.3))') left, x(i), fun
 !!$       WRITE(*,'(i5, 12(1pe12.3))') i, arow
     END DO
     DEALLOCATE(arow, fun)
 !
 !!$    PRINT*, 'Matrix U, V'
 !!$    DO i=1,rank
 !!$       WRITE(*, '(i5,12(1pe12.3))') i, sp%matp%matu(i,:), sp%matp%matvt(:,i)
 !!$    END DO
 !!$    ALLOCATE(arr2d(rank,rank))
 !!$    arr2d = MATMUL(sp%matp%matu, sp%matp%matvt)
 !!$    PRINT*, 'Product U*V^T'
 !!$    DO i=1,rank
 !!$       WRITE(*, '(i5,12(1pe12.3))') i, arr2d(i,:)
 !!$    END DO
 !!$    DEALLOCATE(arr2d)
 !________________________________________________________________________________
 !
 !     Factorisation
 !
 ! Factor A
     CALL factor(sp%matp%mat)
 !
 !  For constant and linear splines, A is diagnonal!
 !  Should skip the rest
 !
     IF( kl.EQ.0 .OR. ku.EQ.0 ) THEN 
        RETURN 
     END IF
 !
 ! U <-- A^(-1) * U
     CALL bsolve(sp%matp%mat, sp%matp%matu)
 !
 ! H <-- 1 + V^T * U
     mr = SIZE(sp%matp%matvt, 1)
     nc = SIZE(sp%matp%matvt, 2)
     CALL init(mr, 0, hmat)  ! hmat is initialized to 0!
     DO i=1,mr
        hmat%val(i,i) = one
     END DO
     CALL dgemm('N', 'N', mr, mr, nc, one, sp%matp%matvt, mr, &
          &      sp%matp%matu, nc, one, hmat%val, mr)
 !
 !!$    hmat%val = MATMUL(sp%matp%matvt, sp%matp%matu)
 !!$    DO i=1,kl+ku
 !!$       hmat%val(i,i) = 1.0d0 + hmat%val(i,i)
 !!$    END DO
 !
 ! V^T <-- H^(-1) V^T
     CALL factor(hmat)
     CALL bsolve(hmat, sp%matp%matvt)
     CALL destroy(hmat)
 !
   END SUBROUTINE splcoefp_setup
 !===========================================================================
   SUBROUTINE splcoef1(sp, f, c, fbc)
 !
 !   Compute the spline coefficients c from grid values f and BC fbc
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in)  :: f(:)
     DOUBLE PRECISION, INTENT(out) :: c(:)
     DOUBLE PRECISION, INTENT(in), OPTIONAL :: fbc(:,:)
     INTEGER :: p, dim, i, ishift
 !
     p = sp%order-1
     dim = sp%dim
 !
 !    BC at left and right boundary
     ishift = 0
     IF( PRESENT(fbc) ) THEN
        DO i=1,p/2
           c(i)       = fbc(1,i)     ! Left boundary
           c(dim-i+1) = fbc(2,i)     ! Right boundary
        END DO
        ishift = p/2
     END IF
 !
 !    Interior points
     DO i=1,sp%nsites
        c(i+ishift) = f(i)
     END DO
 !    WRITE(*,'(a/(13f8.3))') 'RHS', c
 !
 !    Solve for the interpolation coefs. using the factored sp%mat
     CALL bsolve(sp%mat, c)
 !
   END SUBROUTINE splcoef1
 !===========================================================================
   SUBROUTINE splcoefn(sp, f, c, fbc)
 !
 !   Compute the spline coefficients c from grid values f and BC fbc
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in)  :: f(:,:)
     DOUBLE PRECISION, INTENT(out) :: c(:,:)
     DOUBLE PRECISION, INTENT(in), OPTIONAL :: fbc(:,:,:)
     INTEGER :: p, dim, i, ishift
 !
     p = sp%order-1
     dim = sp%dim
 !
 !    BC at left and right boundary
     ishift = 0
     IF( PRESENT(fbc) ) THEN
        DO i=1,p/2
           c(i,:)       = fbc(1,i,:)     ! Left boundary
           c(dim-i+1,:) = fbc(2,i,:)     ! Right boundary
        END DO
        ishift = p/2
     END IF
 !
 !    Interior points   ! c(:,j) for j>SIZE(f,2) could be anything 
     DO i=1,sp%nsites   ! (periodicity in the 2nd dimension)!
        c(i+ishift,1:SIZE(f,2)) = f(i,:)
     END DO
 !!$    WRITE(*,'(a/(13f8.3))') 'RHS', c
 !
 !    Solve for the interpolation coefs. using the factored sp%mat
     CALL bsolve(sp%mat, c)
 !
   END SUBROUTINE splcoefn
 !===========================================================================
   SUBROUTINE splcoefp1(sp, f, c)
 !
 !   Compute the spline coefficient c from grid values f
 !   f(x) is periodic
 !
     USE matrix
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: f(:)
     DOUBLE PRECISION, INTENT(out) :: c(:)
 !
     DOUBLE PRECISION, POINTER  :: arow(:), brow(:)
     DOUBLE PRECISION, PARAMETER :: zero=0.0d0, one=1.0d0, minus1=-1.0d0
     INTEGER :: dim, p, rank, bandw
     INTEGER :: i, j
 !________________________________________________________________________________
 !
     p     = sp%order-1
     rank  = sp%nints
     bandw = SIZE(sp%matp%matvt,1)
 !
 !    Solve the interpolation system
 !
 !  Solve Ay = f
     ALLOCATE(arow(rank), brow(bandw))
 !
     arow(1:rank) = f(1:rank)
     CALL bsolve(sp%matp%mat, arow)
 !
 !  For constant and linear splines, A is diagnonal!
 !  Should skip the rest
 !
     IF( p.LE.1 ) GOTO 100
 !
 !
 !  t = V^T*y
     CALL dgemv('N', bandw, rank, one, sp%matp%matvt, bandw, arow, 1, zero, &
          &      brow, 1)
 !
 !  y = y - Ut
     CALL dgemv('N', rank, bandw, minus1, sp%matp%matu, rank, brow, 1, one, &
          &      arow, 1)
 !
 100 CONTINUE
 !
 !  Interpolation coefficients
     dim   = sp%dim
     DO i=1,dim
        j = MODULO(i-1-p/2, rank) + 1
        c(i) = arow(j)
     END DO
 !
     DEALLOCATE(arow,brow)
 !
   END SUBROUTINE splcoefp1
 !===========================================================================
   SUBROUTINE splcoefpn(sp, f, c)
 !
 !   Compute the spline coefficient c from grid values f
 !   f(x) is periodic
 !
     USE matrix
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: f(:,:)
     DOUBLE PRECISION, INTENT(out) :: c(:,:)
 !
     DOUBLE PRECISION, POINTER  :: arow(:,:), brow(:,:)
     DOUBLE PRECISION, PARAMETER :: zero=0.0d0, one=1.0d0, minus1=-1.0d0
     INTEGER :: p, dim, rank, bandw, nrhs
     INTEGER :: i, j, k
 !________________________________________________________________________________
 !
     p     = sp%order-1
     rank  = sp%nints
     bandw = SIZE(sp%matp%matvt,1)
     nrhs = SIZE(f,2)
 !
 !
 !    Solve the interpolation system
 !
 !  Solve Ay = f
     ALLOCATE(arow(rank,nrhs), brow(bandw,nrhs))
 !
     arow(1:rank,1:nrhs) = f(1:rank,1:nrhs)
     CALL bsolve(sp%matp%mat, arow)
 !
 !  For constant and linear splines, A is diagnonal!
 !  Should skip the rest
 !
     IF( p.LE.1 ) GOTO 100
 !
 !
 !  t = V^T*y
     CALL dgemm('N', 'N', bandw, nrhs, rank, one, sp%matp%matvt, bandw, arow, &
          &      rank, zero, brow, bandw)
 !
 !  y = y - Ut
     CALL dgemm('N', 'N', rank, nrhs, bandw, minus1, sp%matp%matu, rank, brow, &
          &      bandw, one, arow, rank)
 !
 100 CONTINUE
 !
 !  Interpolation coefficients
     dim   = sp%dim
     DO k=1,nrhs
        DO i=1,dim
           j = MODULO(i-1-p/2, rank) + 1
           c(i,k) = arow(j,k)
        END DO
     END DO
 !
     DEALLOCATE(arow,brow)
 !
   END SUBROUTINE splcoefpn
 !
 !===========================================================================
   SUBROUTINE topp0(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(:)
     DOUBLE PRECISION, INTENT(out) :: ppform(0:,:)
     INTEGER :: p, nints, i, j, k
 !
     p = sp%order - 1
     nints = sp%nints
 !
     ppform = 0.0d0
     DO i=1,nints       ! on each knot interval
        DO j=1,p+1      ! all spline in interval i
           DO k=0,p     ! k_th derivatives
              ppform(k,i) = ppform(k,i) + sp%val0(k,j,i)*c(j+i-1)
           END DO
        END DO
     END DO
 !
   END SUBROUTINE topp0
 !===========================================================================
   SUBROUTINE topp0z(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE COMPLEX, INTENT(in) :: c(:)
     DOUBLE COMPLEX, INTENT(out) :: ppform(0:,:)
     INTEGER :: p, nints, i, j, k
 !
     p = sp%order - 1
     nints = sp%nints
 !
     ppform = (0.0d0, 0.0d0)
     DO i=1,nints       ! on each knot interval
        DO j=1,p+1      ! all spline in interval i
           DO k=0,p     ! k_th derivatives
              ppform(k,i) = ppform(k,i) + sp%val0(k,j,i)*c(j+i-1)
           END DO
        END DO
     END DO
 !
   END SUBROUTINE topp0z
 !===========================================================================
   SUBROUTINE topp1(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(:,:)
     DOUBLE PRECISION, INTENT(out) :: ppform(:,:,:)
     INTEGER :: m
 !
     DO m=1,SIZE(c,2)
        CALL topp0(sp, c(:,m), ppform(m,:,:))
     END DO
 !
   END SUBROUTINE topp1
 !===========================================================================
   SUBROUTINE topp1z(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE COMPLEX, INTENT(in) :: c(:,:)
     DOUBLE COMPLEX, INTENT(out) :: ppform(:,:,:)
     INTEGER :: m
 !
     DO m=1,SIZE(c,2)
        CALL topp0z(sp, c(:,m), ppform(m,:,:))
     END DO
 !
   END SUBROUTINE topp1z
 !===========================================================================
   SUBROUTINE topp2(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(:,:,:)
     DOUBLE PRECISION, INTENT(out) :: ppform(:,:,:,:)
     INTEGER :: m, mm
 !
     DO mm=1,SIZE(c,3)
        DO m=1,SIZE(c,2)
           CALL topp0(sp, c(:,m,mm), ppform(m,mm,:,:))
        END DO
     END DO
 !
   END SUBROUTINE topp2
 !===========================================================================
   SUBROUTINE topp2z(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE COMPLEX, INTENT(in) :: c(:,:,:)
     DOUBLE COMPLEX, INTENT(out) :: ppform(:,:,:,:)
     INTEGER :: m, mm
 !
     DO mm=1,SIZE(c,3)
        DO m=1,SIZE(c,2)
           CALL topp0z(sp, c(:,m,mm), ppform(m,mm,:,:))
        END DO
     END DO
 !
   END SUBROUTINE topp2z
 !===========================================================================
   SUBROUTINE topp3(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:,:,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(:,:,:,:)
     DOUBLE PRECISION, INTENT(out) :: ppform(:,:,:,:,:)
     INTEGER :: m, mm, mmm
 !
     DO mmm=1,SIZE(c,4)
        DO mm=1,SIZE(c,3)
           DO m=1,SIZE(c,2)
                  CALL topp0(sp, c(:,m,mm,mmm), ppform(m,mm,mmm,:,:))
           END DO
        END DO
     END DO
 !
   END SUBROUTINE topp3
 !===========================================================================
   SUBROUTINE topp3z(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:,:,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE COMPLEX, INTENT(in) :: c(:,:,:,:)
     DOUBLE COMPLEX, INTENT(out) :: ppform(:,:,:,:,:)
     INTEGER :: m, mm, mmm
 !
     DO mmm=1,SIZE(c,4)
        DO mm=1,SIZE(c,3)
           DO m=1,SIZE(c,2)
                  CALL topp0z(sp, c(:,m,mm,mmm), ppform(m,mm,mmm,:,:))
           END DO
        END DO
     END DO
 !
   END SUBROUTINE topp3z
 !===========================================================================
   SUBROUTINE topp4(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(:,:,:,:,:)
     DOUBLE PRECISION, INTENT(out) :: ppform(:,:,:,:,:,:)
     INTEGER :: m, mm, mmm, mmmm
 !
     DO mmmm=1,SIZE(c,5)
        DO mmm=1,SIZE(c,4)
           DO mm=1,SIZE(c,3)
              DO m=1,SIZE(c,2)
                 CALL topp0(sp, c(:,m,mm,mmm,mmmm), ppform(m,mm,mmm,mmmm,:,:))
              END DO
           END DO
        END DO
     END DO
 !
   END SUBROUTINE topp4
 !===========================================================================
   SUBROUTINE topp4z(sp, c, ppform)
 !
 !   Compute PPFORM of a fuction defined by the spline SP
 !   and spline coefficients C(1:d,:,:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE COMPLEX, INTENT(in) :: c(:,:,:,:,:)
     DOUBLE COMPLEX, INTENT(out) :: ppform(:,:,:,:,:,:)
     INTEGER :: m, mm, mmm, mmmm
 !
     DO mmmm=1,SIZE(c,5)
        DO mmm=1,SIZE(c,4)
           DO mm=1,SIZE(c,3)
              DO m=1,SIZE(c,2)
                 CALL topp0z(sp, c(:,m,mm,mmm,mmmm), ppform(m,mm,mmm,mmmm,:,:))
              END DO
           END DO
        END DO
     END DO
 !
   END SUBROUTINE topp4z
 !===========================================================================
   SUBROUTINE ppval0(sp, x, ppform, left, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !    f(x) = \sum_{k=0}^p ppform(k)*(x-t_{left})^k
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x, ppform(:)
     INTEGER, INTENT(in) :: left, jder
     DOUBLE PRECISION, INTENT(out) :: f
     DOUBLE PRECISION :: h, fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h = x-sp%knots(left)
     f = 0.0d0
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f = f*h + ppform(j)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f = f*h + j*ppform(j+1)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f = f/fact*j*h + ppform(j)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f = f*j
        END DO
     END SELECT
   END SUBROUTINE ppval0
 !===========================================================================
   SUBROUTINE ppval0z(sp, x, ppform, left, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !    f(x) = \sum_{k=0}^p ppform(k)*(x-t_{left})^k
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x
     DOUBLE COMPLEX, INTENT(in) :: ppform(:)
     INTEGER, INTENT(in) :: left, jder
     DOUBLE COMPLEX, INTENT(out) :: f
     DOUBLE PRECISION :: h, fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h = x-sp%knots(left)
     f = (0.0d0,0.0d0)
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f = f*h + ppform(j)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f = f*h + j*ppform(j+1)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f = f/fact*j*h + ppform(j)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f = f*j
        END DO
     END SELECT
   END SUBROUTINE ppval0z
 !===========================================================================
   SUBROUTINE ppval0z_n(sp, x, ppform, left, jder, f)
 !
 !   PPVAL0Z for many points x(:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x(:)
     DOUBLE COMPLEX, INTENT(in) :: ppform(:,:)
     INTEGER, INTENT(in) :: left(:), jder
     DOUBLE COMPLEX, INTENT(out) :: f(:)
     DOUBLE PRECISION :: h(SIZE(x)), fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h(:) = x(:)-sp%knots(left(:))
     f(:) = 0.0d0
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f(:) = f(:)*h(:) + ppform(:,j)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f(:) = f(:)*h(:) + j*ppform(:,j+1)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f(:) = f(:)/fact*j*h(:) + ppform(:,j)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f(:) = f(:)*j
        END DO
     END SELECT
   END SUBROUTINE ppval0z_n
 !===========================================================================
   SUBROUTINE ppval0_n(sp, x, ppform, left, jder, f)
 !
 !   PPVAL0 for many points x(:)
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x(:), ppform(:,:)
     INTEGER, INTENT(in) :: left(:), jder
     DOUBLE PRECISION, INTENT(out) :: f(:)
     DOUBLE PRECISION :: h(SIZE(x)), fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h(:) = x(:)-sp%knots(left(:))
     f(:) = 0.0d0
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f(:) = f(:)*h(:) + ppform(:,j)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f(:) = f(:)*h(:) + j*ppform(:,j+1)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f(:) = f(:)/fact*j*h(:) + ppform(:,j)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f(:) = f(:)*j
        END DO
     END SELECT
   END SUBROUTINE ppval0_n
 !===========================================================================
   SUBROUTINE ppval1(sp, x, ppform, left, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !    f(x) = \sum_{k=0}^p ppform(k)*(x-t_{left})^k
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x, ppform(:,:)
     INTEGER, INTENT(in) :: left, jder
     DOUBLE PRECISION, INTENT(out) :: f(:)
     DOUBLE PRECISION :: h, fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h = x-sp%knots(left)
     f = 0.0d0
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f(:) = f(:)*h + ppform(j,:)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f(:) = f(:)*h + j*ppform(j+1,:)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f(:) = f(:)/fact*j*h + ppform(j,:)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f(:) = f(:)*j
        END DO
     END SELECT
   END SUBROUTINE ppval1
 !===========================================================================
   SUBROUTINE ppval1z(sp, x, ppform, left, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !    f(x) = \sum_{k=0}^p ppform(k)*(x-t_{left})^k
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x
     DOUBLE COMPLEX, INTENT(in) :: ppform(:,:)
     INTEGER, INTENT(in) :: left, jder
     DOUBLE COMPLEX, INTENT(out) :: f(:)
     DOUBLE PRECISION :: h, fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h = x-sp%knots(left)
     f = (0.0d0,0.0d0)
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f(:) = f(:)*h + ppform(j,:)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f(:) = f(:)*h + j*ppform(j+1,:)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f(:) = f(:)/fact*j*h + ppform(j,:)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f(:) = f(:)*j
        END DO
     END SELECT
   END SUBROUTINE ppval1z
 !===========================================================================
   SUBROUTINE ppval2(sp, x, ppform, left, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !    f(x) = \sum_{k=0}^p ppform(k)*(x-t_{left})^k
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x, ppform(:,:,:)
     INTEGER, INTENT(in) :: left, jder
     DOUBLE PRECISION, INTENT(out) :: f(:,:)
     DOUBLE PRECISION :: h, fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h = x-sp%knots(left)
     f = 0.0d0
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f(:,:) = f(:,:)*h + ppform(j,:,:)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f(:,:) = f(:,:)*h + j*ppform(j+1,:,:)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f(:,:) = f(:,:)/fact*j*h + ppform(j,:,:)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f(:,:) = f(:,:)*j
        END DO
     END SELECT
   END SUBROUTINE ppval2
 !===========================================================================
   SUBROUTINE ppval2z(sp, x, ppform, left, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !    f(x) = \sum_{k=0}^p ppform(k)*(x-t_{left})^k
 !
     TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x
     DOUBLE COMPLEX, INTENT(in) :: ppform(:,:,:)
     INTEGER, INTENT(in) :: left, jder
     DOUBLE COMPLEX, INTENT(out) :: f(:,:)
     DOUBLE PRECISION :: h, fact
     INTEGER :: j, order
 !
     order = sp%order   ! Polynomial degree p + 1
 !
     h = x-sp%knots(left)
     f = (0.0d0,0.0d0)
     IF( jder .LT. 0 .OR. jder .GE. order ) RETURN
 !
     SELECT CASE (jder)
     CASE(0)            ! function value
        DO j=order,1,-1
           f(:,:) = f(:,:)*h + ppform(j,:,:)
        END DO
     CASE(1)            ! 1st derivative
        DO j=order-1,1,-1
           f(:,:) = f(:,:)*h + j*ppform(j+1,:,:)
        END DO
     CASE default       ! 2nd and higher derivatives
        fact = order-jder
        DO j=order,jder+1,-1
           f(:,:) = f(:,:)/fact*j*h + ppform(j,:,:)
           fact = fact-1.0d0
        END DO
        DO j=2,jder
           f(:,:) = f(:,:)*j
        END DO
     END SELECT
   END SUBROUTINE ppval2z
 !===========================================================================
   SUBROUTINE locintv0_old(sp, x, left)
 !
 !   Locate the interval containing x
 !   Should be in [0, nints-1]
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: x
     INTEGER, INTENT(out) :: left
     DOUBLE PRECISION :: hinv
     INTEGER :: nints
 !
     nints = sp%nints
 !
 !  Case of equidistant mesh
     IF( sp%nlequid) THEN
        hinv = sp%hinv
        left = MAX(0,MIN(FLOOR((x-sp%knots(0))*hinv),nints-1)) 
        RETURN
     END IF
 !
 !  Non-equistant mesh
     left = sp%left
     DO
        IF( left .EQ. nints ) THEN 
           left = nints-1
           EXIT
        END IF
        IF( left .LT. 0 ) THEN 
           left = 0
           EXIT
        END IF
        IF( x .LT. sp%knots(left+1) ) THEN
           IF( x .GE. sp%knots(left) ) THEN
              EXIT
           ELSE
              left = left-1
           END IF
        ELSE
           left = left+1
        END IF
     END DO
     IF(left .GT. 0 .AND. left .LT. nints) THEN
        sp%left = left
     END IF
   END SUBROUTINE locintv0_old
 !
 !===========================================================================
   SUBROUTINE locintv1_old(sp, x, left)
 !
 !   Locate the intervals left(:) containing x(:)
 !   Should be in [0, nints-1]
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: x(:)
     INTEGER, INTENT(out) :: left(:)
     DOUBLE PRECISION :: hinv
     INTEGER :: nints, i
 !
 !  Case of equidistant mesh
     nints = sp%nints
     IF( sp%nlequid) THEN
        hinv = sp%hinv
        left(:) = MAX(0,MIN(FLOOR((x(:)-sp%knots(0))*hinv),nints-1))
        RETURN
     END IF
 !
 !  Non-equistant mesh
     DO i=1,SIZE(x)
        CALL locintv0_old(sp, x(i), left(i))
     END DO
   END SUBROUTINE locintv1_old
 !
 !===========================================================================
   SUBROUTINE locintv0(sp, x, left)
 !
 !   Locate the interval containing x
 !   Should be in [0, nints-1]
 !
-    TYPE(spline1d) :: sp
+    TYPE(spline1d), INTENT(in) :: sp
     DOUBLE PRECISION, INTENT(in) :: x
     INTEGER, INTENT(out) :: left
     DOUBLE PRECISION :: hinv
     INTEGER :: l, nf, nints
 !
     nints = sp%nints
 !
 !  Case of equidistant mesh
     IF( sp%nlequid) THEN
        hinv = sp%hinv
        left = MAX(0,MIN(FLOOR((x-sp%knots(0))*hinv),nints-1)) 
        RETURN
     END IF
 !
 !  Non-equistant mesh
     hinv = sp%hinv
     nf = SIZE(sp%fmap) - 1
     l = MAX(0,MIN(FLOOR((x-sp%knots(0))*hinv),nf-1))   ! left on fine mesh
     left = sp%fmap(l)
     IF( x.GE.sp%knots(left+1) ) left = MIN(left+1,nints-1)
   END SUBROUTINE locintv0
 !
 !===========================================================================
   SUBROUTINE locintv1(sp, x, left)
 !
 !   Locate the intervals left(:) containing x(:)
 !   Should be in [0, nints-1]
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: x(:)
     INTEGER, INTENT(out) :: left(:)
     INTEGER :: l(SIZE(x))
     DOUBLE PRECISION :: hinv
     INTEGER :: npt, nf, nints, i
 !
     npt = SIZE(x)
 !
 !  Case of equidistant mesh
     nints = sp%nints
     IF( sp%nlequid) THEN
        hinv = sp%hinv
        left(1:npt) = MAX(0,MIN(FLOOR((x(1:npt)-sp%knots(0))*hinv),nints-1))
        RETURN
     END IF
 !
 !  Non-equistant mesh
     hinv = sp%hinv
     nf = SIZE(sp%fmap) - 1
     l(:) = MAX(0,MIN(FLOOR((x(:)-sp%knots(0))*hinv),nf-1))   ! left on fine mesh
     left(1:npt) = sp%fmap(l(1:npt))
     WHERE( x.GE.sp%knots(left+1) ) left = MIN(left+1,nints-1)
   END SUBROUTINE locintv1
 !
 !===========================================================================
   SUBROUTINE destroy_sp1d(sp)
 !
 !  Clean up 1d spline object
 !
     TYPE(spline1d) :: sp
 !
     IF( ASSOCIATED(sp%knots) ) DEALLOCATE (sp%knots)
     IF( ASSOCIATED(sp%val0) ) DEALLOCATE (sp%val0)
     IF( ASSOCIATED(sp%valc) ) DEALLOCATE (sp%valc)
     IF( ASSOCIATED(sp%gausx) ) DEALLOCATE (sp%gausx)
     IF( ASSOCIATED(sp%gausw) ) DEALLOCATE (sp%gausw)
     IF( ASSOCIATED(sp%intspl) ) DEALLOCATE (sp%intspl)
     IF( ASSOCIATED(sp%ppform) ) DEALLOCATE (sp%ppform)
     IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE (sp%ppformz)
     IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
     IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
     IF( ASSOCIATED(sp%fmap) ) DEALLOCATE(sp%fmap)
 !
     CALL destroy(sp%mat)
     CALL destroy(sp%matp)
     CALL destroy_dftmap(sp%dft)
   END SUBROUTINE destroy_sp1d
 !
 !===========================================================================
   SUBROUTINE destroy_dftmap(m)
 !
 !   Clean up DFTMAP
 !
     TYPE(dftmap) :: m
 !
     IF(ASSOCIATED(m%coefs)) DEALLOCATE(m%coefs)
   END SUBROUTINE destroy_dftmap
 !===========================================================================
   SUBROUTINE destroy_sp2d(sp)
 !
 !  Clean up 2d spline object
 !
     TYPE(spline2d) :: sp
 !
     IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
     IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE(sp%ppformz)
     IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
     IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
     CALL destroy_sp1d(sp%sp1)
     CALL destroy_sp1d(sp%sp2)
   END SUBROUTINE destroy_sp2d
 !===========================================================================
   SUBROUTINE destroy_sp2d1d(sp)
 !
 !  Clean up 2d1d spline object
 !
     TYPE(spline2d1d) :: sp
 !
     IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
     IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE (sp%ppformz)
     IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
     IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
     CALL destroy_sp2d(sp%sp12)
     CALL destroy_sp1d(sp%sp3)
   END SUBROUTINE destroy_sp2d1d
 !
 !===========================================================================
   SUBROUTINE calc_integ0(sp, finteg)
 !
 !   Compute integral of splines
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(out) :: finteg(0:)
     DOUBLE PRECISION, ALLOCATABLE :: xg(:), wg(:), fun(:,:)
     DOUBLE PRECISION :: x1, x2
     INTEGER :: dim, nx, nidbas, ng, i, ig, j, jj, left
 !
     CALL get_dim(sp, dim, nx, nidbas)
     ng = MAX(2, (nidbas+2)/2)
     ALLOCATE(xg(ng), wg(ng), fun(0:nidbas,1))
     fun = 0.0d0
     finteg = 0.0d0
     DO i=1,nx             ! Loop thru the intervals
        left = i
        x1 = sp%knots(i-1)
        x2 = sp%knots(i)
        CALL gauleg(x1, x2, xg, wg, ng)
        DO ig=1,ng         ! Loop thru Gauss points
           CALL basfun(xg(ig), sp, fun, i)
           left = i-1
           DO j=0,nidbas   ! Loop thru the splines [left:left+nidbas]
              jj = left+j  ! in this interval
              IF( sp%period ) jj = MODULO(left+j, nx)
              finteg(jj) = finteg(jj) + wg(ig)*fun(j,1)
           END DO
        END DO
     END DO
     DEALLOCATE(xg, wg, fun)
   END SUBROUTINE calc_integ0
 !===========================================================================
   SUBROUTINE calc_integn(sp, finteg)
 !
 !   Compute integrals = Int( x^a \Lambda_j(x) ), a=0,1,... n
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(out) :: finteg(0:,0:)
     DOUBLE PRECISION, ALLOCATABLE :: xg(:), wg(:), fun(:,:)
     DOUBLE PRECISION :: x1, x2, xpow
     INTEGER :: dim, nx, nidbas, ng, i, ig, j, k, jj, left
     INTEGER :: nord
 !
     nord = SIZE(finteg,2)-1
     CALL get_dim(sp, dim, nx, nidbas)
     ng = MAX(2, (nidbas+nord+2)/2)
     ALLOCATE(xg(ng), wg(ng), fun(0:nidbas,1))
     fun = 0.0d0
     finteg = 0.0d0
     DO i=1,nx             ! Loop thru the intervals
        left = i
        x1 = sp%knots(i-1)
        x2 = sp%knots(i)
        CALL gauleg(x1, x2, xg, wg, ng)
        DO ig=1,ng         ! Loop thru Gauss points
           CALL basfun(xg(ig), sp, fun, i)
           left=i-1
           DO j=0,nidbas   ! Loop thru the splines [left:left+nidbas]
              jj = left+j  ! in this interval
              IF( sp%period ) jj = MODULO(left+j, nx)
              xpow = wg(ig)*fun(j,1)
              DO k=0,nord
                 finteg(jj,k) = finteg(jj,k) + xpow
                 xpow = xpow*xg(ig)
              END DO
           END DO
        END DO
     END DO
     DEALLOCATE(xg, wg, fun)
   END SUBROUTINE calc_integn
 !===========================================================================
 !
   DOUBLE PRECISION FUNCTION fintg1(sp, c)
 !
 !   Integral of 1d function from its spline coefs c.
 !
     TYPE(spline1d) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(0:)
     INTEGER :: dim
     dim = sp%dim
     fintg1 = DOT_PRODUCT(sp%intspl(0:dim-1), c(0:dim-1))
   END FUNCTION fintg1
 !===========================================================================
   DOUBLE PRECISION FUNCTION fintg2(sp, c)
 !
 !   Integral of 2d function from its spline coefs c.
 !
     TYPE(spline2d) :: sp
     DOUBLE PRECISION, INTENT(in) :: c(0:,0:)
     INTEGER :: dim1, dim2, i, j
     dim1 = sp%sp1%dim
     dim2 = sp%sp2%dim
     fintg2 = 0.0d0
     DO j=0,dim2-1
        DO i=0,dim1-1
           fintg2 = fintg2 + c(i,j)*sp%sp1%intspl(i)*sp%sp2%intspl(j)
        END DO
     END DO
   END FUNCTION fintg2
 !===========================================================================
   SUBROUTINE gridval2d_2d(sp, xp, yp, fp, jder, c, ppform)
 !
 !  Compute values or jder-th dervivative of f(x,y) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients c are given.
 !
 !              F(I,J) = F(X(I), Y(J))
 !
     TYPE(spline2d), INTENT(inout) :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp, yp
     DOUBLE PRECISION, DIMENSION(:,:), INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(2)
     DOUBLE PRECISION, DIMENSION(:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE PRECISION, DIMENSION(:,:,:,:), OPTIONAL :: ppform
 !
     INTEGER :: d1, d2, k1, k2, n1, n2
     DOUBLE PRECISION, ALLOCATABLE :: work(:,:,:), temp(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp))
     INTEGER :: leftx(SIZE(xp)), lefty(SIZE(yp))
     INTEGER :: i, j, k, ii, jj
     LOGICAL :: nlppform
 !
     d1 = sp%sp1%dim
     d2 = sp%sp2%dim
     k1 = sp%sp1%order
     k2 = sp%sp2%order
     n1 = sp%sp1%nints
     n2 = sp%sp2%nints
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !   Compute PPFORMM/BCOEFS if spline coefs are passed
 !
     IF( PRESENT(c) ) THEN
        IF( nlppform ) THEN
           ALLOCATE(work(d2,k1,n1))
           CALL topp1(sp%sp1, c , work)
           IF(PRESENT(ppform)) THEN
              CALL topp2(sp%sp2, work, ppform)
           ELSE
              IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
              ALLOCATE(sp%ppform(k1,n1,k2,n2))
              CALL topp2(sp%sp2, work, sp%ppform)
           END IF
           DEALLOCATE(work)
        ELSE
           IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
           ALLOCATE(sp%bcoefs(SIZE(c,1),SIZE(c,2)))
           sp%bcoefs = c
        END IF
     END IF
 !
 !   Applly periodicity if required
 !
     IF( sp%sp1%period ) THEN   !  ** Applly periodicity **
        x = sp%sp1%knots(0) + MODULO(xp-sp%sp1%knots(0), sp%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp%sp2%period ) THEN   !  ** Applly periodicity **
        y = sp%sp2%knots(0) + MODULO(yp-sp%sp2%knots(0), sp%sp2%lperiod)
     ELSE
        y = yp
     END IF
 !
 !   Locate interval containing (x,y)
 !
     CALL locintv(sp%sp1, x, leftx)
     CALL locintv(sp%sp2, y, lefty)
 !
 !  Compute function/derivatives
 !
     IF( nlppform ) THEN   ! using PP form
        ALLOCATE(temp(k2))
        DO j=1,SIZE(y)
           DO i=1,SIZE(x)
              IF(PRESENT(ppform)) THEN
                 CALL ppval(sp%sp1, x(i), ppform(:,leftx(i)+1,:,lefty(j)+1),&
                      &     leftx(i), jder(1), temp)
              ELSE
                 CALL ppval(sp%sp1, x(i), sp%ppform(:,leftx(i)+1,:,lefty(j)+1),&
                      &     leftx(i), jder(1), temp)
              END IF
              CALL ppval(sp%sp2, y(j), temp, lefty(j), jder(2), fp(i,j))
           END DO
        END DO
        DEALLOCATE(temp)
     ELSE                   ! using spline expansion
        ALLOCATE(funx(1:k1,0:jder(1)), funy(1:k2,0:jder(2)))
        fp = 0.0d0
        DO j=1,SIZE(y)
           CALL basfun(y(j), sp%sp2, funy, lefty(j)+1)
           DO i=1,SIZE(x)
              CALL basfun(x(i), sp%sp1, funx, leftx(i)+1)
              DO jj=1,k2
                 DO ii=1,k1
                    fp(i,j) = fp(i,j) + sp%bcoefs(leftx(i)+ii,lefty(j)+jj) * &
                         &   funx(ii,jder(1))*funy(jj,jder(2))
                 END DO
              END DO
           END DO
        END DO
        DEALLOCATE(funx, funy)
     END IF
   END SUBROUTINE gridval2d_2d
 !===========================================================================
   SUBROUTINE gridval2d1d_3d(sp, xp, yp, zp, fp, jder, c, ppform)
 !
 !  Compute values or jder-th dervivative of f(x,y,z) from spline
 !  coefficients (nlppform=.false.) or ppform (nlppform=.true.)
 !
 !              F(I,J,K) = F(X(I), Y(J), Z(K))
 !
     TYPE(spline2d1d), TARGET :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp, yp, zp
     DOUBLE PRECISION, DIMENSION(:,:,:), INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(3)
     DOUBLE PRECISION, DIMENSION(:,:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE PRECISION, DIMENSION(:,:,:,:,:,:), OPTIONAL :: ppform
 !
     TYPE(spline2d), POINTER :: sp2
     DOUBLE PRECISION, ALLOCATABLE :: work1(:,:,:,:), work2(:,:,:,:,:)
     DOUBLE PRECISION, ALLOCATABLE :: temp1(:,:), temp2(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:), funz(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp)), z(SIZE(zp))
     INTEGER          :: leftx(SIZE(xp)), lefty(SIZE(yp)), leftz(SIZE(zp))
     INTEGER :: d1, d2, d3, k1, k2, k3, n1, n2, n3
     INTEGER :: ipx, ipy, ipz, k, ii, jj, kk
     LOGICAL :: nlppform
 !--------------------------------------------------------------------------------
 !                               1. Prologue
 !
     sp2 => sp%sp12
     d1 = sp2%sp1%dim
     d2 = sp2%sp2%dim
     d3 = sp%sp3%dim
     k1 = sp2%sp1%order
     k2 = sp2%sp2%order
     k3 = sp%sp3%order
     n1 = sp2%sp1%nints
     n2 = sp2%sp2%nints
     n3 = sp%sp3%nints
     nlppform = sp2%sp1%nlppform .OR. sp2%sp2%nlppform .OR. sp%sp3%nlppform
 !
 !   Applly periodicity if required
     IF( sp2%sp1%period ) THEN
        x = sp2%sp1%knots(0) + MODULO(xp-sp2%sp1%knots(0), sp2%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp2%sp2%period ) THEN
        y = sp2%sp2%knots(0) + MODULO(yp-sp2%sp2%knots(0), sp2%sp2%lperiod)
     ELSE
        y = yp
     END IF
     IF( sp%sp3%period ) THEN
        z = sp%sp3%knots(0) + MODULO(zp-sp%sp3%knots(0), sp%sp3%lperiod)
     ELSE
        z = zp
     END IF
 !
 !   Locate interval containing (x,y,z)
     CALL locintv(sp2%sp1, x, leftx)
     CALL locintv(sp2%sp2, y, lefty)
     CALL locintv(sp%sp3,  z, leftz)
 !--------------------------------------------------------------------------------
 !                               2. Using PPFORM
 !
     IF( nlppform ) THEN
 !
 !   Compute PPFORM from BCOEF
        IF( PRESENT(c) ) THEN
           ALLOCATE(work2(d3,k1,n1,k2,n2))
           ALLOCATE(work1(d2,d3,k1,n1))
           CALL topp2(sp2%sp1, c, work1)
           CALL topp3(sp2%sp2, work1, work2)
           DEALLOCATE(work1)
           IF( PRESENT(ppform) )THEN
              CALL topp4(sp%sp3, work2, ppform)
           ELSE
              IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
              ALLOCATE(sp%ppform(k1,n1,k2,n2,k3,n3))
              CALL topp4(sp%sp3, work2, sp%ppform)
           END IF
           DEALLOCATE(work2)
        END IF
 !
 !   Compute function/derivatives
        ALLOCATE(temp1(k2,k3))
        ALLOCATE(temp2(k3))
        DO ipz=1,SIZE(z)
           DO ipy=1,SIZE(y)
              DO ipx=1,SIZE(x)
                 IF(PRESENT(ppform)) THEN
                    CALL ppval(sp2%sp1, x(ipx), &
                         &  ppform(:,leftx(ipx)+1,:,lefty(ipy)+1,:,leftz(ipz)+1),&
                         &  leftx(ipx), jder(1), temp1)
                 ELSE
                    CALL ppval(sp2%sp1, x(ipx), &
                         &  sp%ppform(:,leftx(ipx)+1,:,lefty(ipy)+1,:,leftz(ipz)+1),&
                         &  leftx(ipx), jder(1), temp1)
                 END IF
                 CALL ppval(sp2%sp2, y(ipy), temp1, lefty(ipy), jder(2), &
                      &     temp2)
                 CALL ppval(sp%sp3, z(ipz), temp2, leftz(ipz), jder(3), &
                      &     fp(ipx,ipy,ipz))
              END DO
           END DO
        END DO
        DEALLOCATE(temp1)
        DEALLOCATE(temp2)
 !--------------------------------------------------------------------------------
 !                               3. Using spline expansion
 !
     ELSE
        IF( PRESENT(c) ) THEN
           IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
           ALLOCATE(sp%bcoefs(SIZE(c,1),SIZE(c,2),SIZE(c,3)))
           sp%bcoefs = c
        END IF
 !
 !   Compute function/derivatives
        ALLOCATE(funx(1:k1,0:jder(1)))
        ALLOCATE(funy(1:k2,0:jder(2)))
        ALLOCATE(funz(1:k3,0:jder(3)))
        fp = 0.0d0
        DO ipz=1,SIZE(z)
           CALL basfun(z(ipz), sp%sp3, funz, leftz(ipz)+1)
           DO ipy=1,SIZE(y)
              CALL basfun(y(ipy), sp2%sp2, funy, lefty(ipy)+1)
              DO ipx=1,SIZE(x)
                 CALL basfun(x(ipx), sp2%sp1, funx, leftx(ipx)+1)
                 DO kk=1,k3
                    DO jj=1,k2
                       DO ii=1,k1
                          fp(ipx,ipy,ipz) = fp(ipx,ipy,ipz) + &
                            &  sp%bcoefs(leftx(ipx)+ii,lefty(ipy)+jj,leftz(ipz)+kk) * &
                            &  funx(ii,jder(1)) * funy(jj,jder(2)) * funz(kk,jder(3))
                       END DO
                    END DO
                 END DO
              END DO
           END DO
        END DO
        DEALLOCATE(funx, funy, funz)
     END IF
   END SUBROUTINE gridval2d1d_3d
 !===========================================================================
   SUBROUTINE gridval2d1d_1d(sp, xp, yp, zp, fp, jder, c, ppform)
 !
 !  Compute values or jder-th dervivative of f(x,y,z) from spline
 !  coefficients (nlppform=.false.) or ppform (nlppform=.true.)
 !
 !              F(I) = F(X(I),Y(I),Z(I))
 !
     TYPE(spline2d1d), TARGET :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp, yp, zp
     DOUBLE PRECISION, DIMENSION(:), INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(3)
     DOUBLE PRECISION, DIMENSION(:,:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE PRECISION, DIMENSION(:,:,:,:,:,:), OPTIONAL :: ppform
 !
     TYPE(spline2d), POINTER :: sp2
     DOUBLE PRECISION, ALLOCATABLE :: work1(:,:,:,:), work2(:,:,:,:,:)
     DOUBLE PRECISION, ALLOCATABLE :: temp1(:,:), temp2(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:), funz(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp)), z(SIZE(zp))
     INTEGER          :: leftx(SIZE(xp)), lefty(SIZE(yp)), leftz(SIZE(zp))
     INTEGER :: d1, d2, d3, k1, k2, k3, n1, n2, n3
     INTEGER :: np, ip, ii, jj, kk
     LOGICAL :: nlppform
 !--------------------------------------------------------------------------------
 !                               1. Prologue
 !
     sp2 => sp%sp12
     d1 = sp2%sp1%dim
     d2 = sp2%sp2%dim
     d3 = sp%sp3%dim
     k1 = sp2%sp1%order
     k2 = sp2%sp2%order
     k3 = sp%sp3%order
     n1 = sp2%sp1%nints
     n2 = sp2%sp2%nints
     n3 = sp%sp3%nints
     np = SIZE(xp)
     nlppform = sp2%sp1%nlppform .OR. sp2%sp2%nlppform .OR. sp%sp3%nlppform
 !
 !   Applly periodicity if required
     IF( sp2%sp1%period ) THEN
        x = sp2%sp1%knots(0) + MODULO(xp-sp2%sp1%knots(0), sp2%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp2%sp2%period ) THEN
        y = sp2%sp2%knots(0) + MODULO(yp-sp2%sp2%knots(0), sp2%sp2%lperiod)
     ELSE
        y = yp
     END IF
     IF( sp%sp3%period ) THEN
        z = sp%sp3%knots(0) + MODULO(zp-sp%sp3%knots(0), sp%sp3%lperiod)
     ELSE
        z = zp
     END IF
 !
 !   Locate interval containing (x,y,z)
     CALL locintv(sp2%sp1, x, leftx)
     CALL locintv(sp2%sp2, y, lefty)
     CALL locintv(sp%sp3,  z, leftz)
 !--------------------------------------------------------------------------------
 !                               2. Using PPFORM
 !
     IF( nlppform ) THEN
 !
 !   Compute PPFORM from BCOEF
        IF( PRESENT(c) ) THEN
           ALLOCATE(work2(d3,k1,n1,k2,n2))
           ALLOCATE(work1(d2,d3,k1,n1))
           CALL topp2(sp2%sp1, c, work1)
           CALL topp3(sp2%sp2, work1, work2)
           DEALLOCATE(work1)
           IF( PRESENT(ppform) )THEN
              CALL topp4(sp%sp3, work2, ppform)
           ELSE
              IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
              ALLOCATE(sp%ppform(k1,n1,k2,n2,k3,n3))
              CALL topp4(sp%sp3, work2, sp%ppform)
           END IF
           DEALLOCATE(work2)
        END IF
 !
 !   Compute function/derivatives
        ALLOCATE(temp1(k2,k3))
        ALLOCATE(temp2(k3))
        DO ip=1,np
           IF(PRESENT(ppform)) THEN
              CALL ppval(sp2%sp1, x(ip), &
                   &  ppform(:,leftx(ip)+1,:,lefty(ip)+1,:,leftz(ip)+1),&
                   &  leftx(ip), jder(1), temp1)
           ELSE
              CALL ppval(sp2%sp1, x(ip), &
                   &  sp%ppform(:,leftx(ip)+1,:,lefty(ip)+1,:,leftz(ip)+1),&
                   &  leftx(ip), jder(1), temp1)
           END IF
           CALL ppval(sp2%sp2, y(ip), temp1, lefty(ip), jder(2), temp2)
           CALL ppval(sp%sp3, z(ip), temp2, leftz(ip), jder(3), fp(ip))
        END DO
        DEALLOCATE(temp1)
        DEALLOCATE(temp2)
 !--------------------------------------------------------------------------------
 !                               3. Using spline expansion
 !
     ELSE
        IF( PRESENT(c) ) THEN
           IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
           ALLOCATE(sp%bcoefs(SIZE(c,1),SIZE(c,2),SIZE(c,3)))
           sp%bcoefs = c
        END IF
 !
 !   Compute function/derivatives
        ALLOCATE(funx(1:k1,0:jder(1)))
        ALLOCATE(funy(1:k2,0:jder(2)))
        ALLOCATE(funz(1:k3,0:jder(3)))
        fp = 0.0d0
        DO ip=1,np
           CALL basfun(x(ip), sp2%sp1, funx, leftx(ip)+1)
           CALL basfun(y(ip), sp2%sp2, funy, lefty(ip)+1)
           CALL basfun(z(ip), sp%sp3,  funz, leftz(ip)+1)
           DO kk=1,k3
              DO jj=1,k2
                 DO ii=1,k1
                    fp(ip) = fp(ip) + &
                            &  sp%bcoefs(leftx(ip)+ii,lefty(ip)+jj,leftz(ip)+kk) * &
                            &  funx(ii,jder(1))*funy(jj,jder(2))*funz(kk,jder(3))
                 END DO
              END DO
           END DO
        END DO
        DEALLOCATE(funx, funy, funz)
     END IF
   END SUBROUTINE gridval2d1d_1d
 !===========================================================================
   SUBROUTINE gridval2d(sp, xp, yp, fp, jder, c, ppform)
 !
 !  Compute values or jder-th dervivative of f(x,y) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients c are given.
 !
 !              F = F(X, Y)
 !
     TYPE(spline2d), INTENT(inout) :: sp
     DOUBLE PRECISION, INTENT(in) :: xp, yp
     DOUBLE PRECISION, INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(2)
     DOUBLE PRECISION, DIMENSION(:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE PRECISION, DIMENSION(:,:,:,:), OPTIONAL :: ppform
 !
     INTEGER :: d1, d2, k1, k2, n1, n2
     DOUBLE PRECISION, ALLOCATABLE :: work(:,:,:), temp(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION :: x, y
     INTEGER :: leftx, lefty
     INTEGER :: i, j, k, ii, jj
     LOGICAL :: nlppform
 !
     d1 = sp%sp1%dim
     d2 = sp%sp2%dim
     k1 = sp%sp1%order
     k2 = sp%sp2%order
     n1 = sp%sp1%nints
     n2 = sp%sp2%nints
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !   Compute PPFORM/BCOEFS if spline coefs are passed
 !
     IF( PRESENT(c)) THEN
        IF( nlppform ) THEN
           ALLOCATE(work(d2,k1,n1))
           CALL topp1(sp%sp1, c , work)
           IF(PRESENT(ppform)) THEN
              CALL topp2(sp%sp2, work, ppform)
           ELSE
              IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
              ALLOCATE(sp%ppform(k1,n1,k2,n2))
              CALL topp2(sp%sp2, work, sp%ppform)
           END IF
           DEALLOCATE(work)
        ELSE
           IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
           ALLOCATE(sp%bcoefs(SIZE(c,1),SIZE(c,2)))
           sp%bcoefs = c
        END IF
     END IF
 !
 !   Applly periodicity if required
 !
     IF( sp%sp1%period ) THEN   !  ** Applly periodicity **
        x = sp%sp1%knots(0) + MODULO(xp-sp%sp1%knots(0), sp%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp%sp2%period ) THEN   !  ** Applly periodicity **
        y = sp%sp2%knots(0) + MODULO(yp-sp%sp2%knots(0), sp%sp2%lperiod)
     ELSE
        y = yp
     END IF
 !
 !  Locate the interval containing x, y
 !
     CALL locintv(sp%sp1, x, leftx)
     CALL locintv(sp%sp2, y, lefty)
 !
 !  Compute function/derivatives
 !
     IF( nlppform ) THEN   ! using PP form
        ALLOCATE(temp(k2))
        IF(PRESENT(ppform)) THEN
           CALL ppval(sp%sp1, x, ppform(:,leftx+1,:,lefty+1),&
                &  leftx, jder(1), temp)
        ELSE
           CALL ppval(sp%sp1, x, sp%ppform(:,leftx+1,:,lefty+1),&
                &  leftx, jder(1), temp)
        END IF
        CALL ppval(sp%sp2, y, temp, lefty, jder(2), fp)
        DEALLOCATE(temp)
     ELSE                   ! using spline expansion
        ALLOCATE(funx(1:k1,0:jder(1)), funy(1:k2,0:jder(2)))
        fp = 0.0d0
        CALL basfun(x, sp%sp1, funx, leftx+1)
        CALL basfun(y, sp%sp2, funy, lefty+1)
        DO jj=1,k2
           DO ii=1,k1
              fp = fp + &
                   &   funy(jj,jder(2))*sp%bcoefs(leftx+ii,lefty+jj)* &
                   &   funx(ii,jder(1))
           END DO
        END DO
        DEALLOCATE(funx, funy)
     END IF
   END SUBROUTINE gridval2d
 !===========================================================================
   SUBROUTINE gridval2d_1d(sp, xp, yp, fp, jder, c, ppform)
 !
 !  Compute values or jder-th dervivative of f(x,y) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients c are given.
 !
 !              F(I) = F(X(I), Y(I))
 !
     TYPE(spline2d), INTENT(inout) :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp, yp
     DOUBLE PRECISION, DIMENSION(:), INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(2)
     DOUBLE PRECISION, DIMENSION(:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE PRECISION, DIMENSION(:,:,:,:), OPTIONAL :: ppform
 !
     INTEGER :: d1, d2, k1, k2, n1, n2, np
     DOUBLE PRECISION, ALLOCATABLE :: work(:,:,:), temp(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp))
     INTEGER :: leftx(SIZE(xp)), lefty(SIZE(yp))
     INTEGER :: i, j, k, ii, jj
     LOGICAL :: nlppform
 !
     d1 = sp%sp1%dim
     d2 = sp%sp2%dim
     k1 = sp%sp1%order
     k2 = sp%sp2%order
     n1 = sp%sp1%nints
     n2 = sp%sp2%nints
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !   Compute PPFORM/BCOEFS if spline coefs are passed
 !
     IF( PRESENT(c)) THEN
        IF( nlppform ) THEN
           ALLOCATE(work(d2,k1,n1))
           CALL topp1(sp%sp1, c , work)
           IF(PRESENT(ppform)) THEN
              CALL topp2(sp%sp2, work, ppform)
           ELSE
              IF( ASSOCIATED(sp%ppform) ) DEALLOCATE(sp%ppform)
              ALLOCATE(sp%ppform(k1,n1,k2,n2))
              CALL topp2(sp%sp2, work, sp%ppform)
           END IF
           DEALLOCATE(work)
        ELSE
           IF( ASSOCIATED(sp%bcoefs) ) DEALLOCATE(sp%bcoefs)
           ALLOCATE(sp%bcoefs(SIZE(c,1),SIZE(c,2)))
           sp%bcoefs = c
        END IF
     END IF
 !
 !   Applly periodicity if required
 !
     np = SIZE(xp)
     IF( sp%sp1%period ) THEN   !  ** Applly periodicity **
        x = sp%sp1%knots(0) + MODULO(xp-sp%sp1%knots(0), sp%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp%sp2%period ) THEN   !  ** Applly periodicity **
        y = sp%sp2%knots(0) + MODULO(yp-sp%sp2%knots(0), sp%sp2%lperiod)
     ELSE
        y = yp
     END IF
 !
 !  Locate the interval containing x, y
 !
     CALL locintv(sp%sp1, x, leftx)
     CALL locintv(sp%sp2, y, lefty)
 !
 !  Compute function/derivatives
 !
     IF( nlppform ) THEN   ! using PP form
        ALLOCATE(temp(k2))
        DO i=1,np
           IF(PRESENT(ppform)) THEN
              CALL ppval(sp%sp1, x(i), ppform(:,leftx(i)+1,:,lefty(i)+1),&
                   &  leftx(i), jder(1), temp)
           ELSE
              CALL ppval(sp%sp1, x(i), sp%ppform(:,leftx(i)+1,:,lefty(i)+1),&
                   &  leftx(i), jder(1), temp)
           END IF
           CALL ppval(sp%sp2, y(i), temp, lefty(i), jder(2), fp(i))
        END DO
        DEALLOCATE(temp)
     ELSE                   ! using spline expansion
        ALLOCATE(funx(1:k1,0:jder(1)), funy(1:k2,0:jder(2)))
        fp = 0.0d0
        DO i=1,np
           CALL basfun(x(i), sp%sp1, funx, leftx(i)+1)
           CALL basfun(y(i), sp%sp2, funy, lefty(i)+1)
           DO jj=1,k2
              DO ii=1,k1
                 fp(i) = fp(i) + &
                    &   funy(jj,jder(2))*sp%bcoefs(leftx(i)+ii,lefty(i)+jj)* &
                    &   funx(ii,jder(1))
              END DO
           END DO
        END DO
        DEALLOCATE(funx, funy)
     END IF
   END SUBROUTINE gridval2d_1d
 !===========================================================================
   SUBROUTINE gridval2dz(sp, xp, yp, fp, jder, c, ppformz)
 !
 !  Compute values or jder-th dervivative of f(x,y) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients c are given.
 !
 !              F = F(X, Y)
 !
     TYPE(spline2d), INTENT(inout) :: sp
     DOUBLE PRECISION, INTENT(in) :: xp, yp
     DOUBLE COMPLEX, INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(2)
     DOUBLE COMPLEX, DIMENSION(:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE COMPLEX, DIMENSION(:,:,:,:), OPTIONAL :: ppformz
 !
     INTEGER :: d1, d2, k1, k2, n1, n2
     DOUBLE COMPLEX, ALLOCATABLE :: work(:,:,:), temp(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION :: x, y
     INTEGER :: leftx, lefty
     INTEGER :: i, j, k, ii, jj
     LOGICAL :: nlppform
 !
     d1 = sp%sp1%dim
     d2 = sp%sp2%dim
     k1 = sp%sp1%order
     k2 = sp%sp2%order
     n1 = sp%sp1%nints
     n2 = sp%sp2%nints
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !   Compute PPFORM/BCOEFS if spline coefs are passed
 !
     IF( PRESENT(c)) THEN
        IF( nlppform ) THEN
           ALLOCATE(work(d2,k1,n1))
           CALL topp1z(sp%sp1, c , work)
           IF(PRESENT(ppformz)) THEN
              CALL topp2z(sp%sp2, work, ppformz)
           ELSE
              IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE(sp%ppformz)
              ALLOCATE(sp%ppformz(k1,n1,k2,n2))
              CALL topp2z(sp%sp2, work, sp%ppformz)
           END IF
           DEALLOCATE(work)
        ELSE
           IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
           ALLOCATE(sp%bcoefsc(SIZE(c,1),SIZE(c,2)))
           sp%bcoefsc = c
        END IF
     END IF
 !
 !   Applly periodicity if required
 !
     IF( sp%sp1%period ) THEN   !  ** Applly periodicity **
        x = sp%sp1%knots(0) + MODULO(xp-sp%sp1%knots(0), sp%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp%sp2%period ) THEN   !  ** Applly periodicity **
        y = sp%sp2%knots(0) + MODULO(yp-sp%sp2%knots(0), sp%sp2%lperiod)
     ELSE
        y = yp
     END IF
 !
 !  Locate the interval containing x, y
 !
     CALL locintv(sp%sp1, x, leftx)
     CALL locintv(sp%sp2, y, lefty)
 !
 !  Compute function/derivatives
 !
     IF( nlppform ) THEN   ! using PP form
        ALLOCATE(temp(k2))
        IF(PRESENT(ppformz)) THEN
           CALL ppval(sp%sp1, x, ppformz(:,leftx+1,:,lefty+1),&
                &  leftx, jder(1), temp)
        ELSE
           CALL ppval(sp%sp1, x, sp%ppformz(:,leftx+1,:,lefty+1),&
                &  leftx, jder(1), temp)
        END IF
        CALL ppval(sp%sp2, y, temp, lefty, jder(2), fp)
        DEALLOCATE(temp)
     ELSE                   ! using spline expansion
        ALLOCATE(funx(1:k1,0:jder(1)), funy(1:k2,0:jder(2)))
        fp = (0.0d0,0.0d0)
        CALL basfun(x, sp%sp1, funx, leftx+1)
        CALL basfun(y, sp%sp2, funy, lefty+1)
        DO jj=1,k2
           DO ii=1,k1
              fp = fp + &
                   &   funy(jj,jder(2))*sp%bcoefsc(leftx+ii,lefty+jj)* &
                   &   funx(ii,jder(1))
           END DO
        END DO
        DEALLOCATE(funx, funy)
     END IF
   END SUBROUTINE gridval2dz
 !===========================================================================
   SUBROUTINE gridval2d_1dz(sp, xp, yp, fp, jder, c, ppformz)
 !
 !  Compute values or jder-th dervivative of f(x,y) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients c are given.
 !
 !              F(I) = F(X(I), Y(I))
 !
     TYPE(spline2d), INTENT(inout) :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp, yp
     DOUBLE COMPLEX, DIMENSION(:), INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(2)
     DOUBLE COMPLEX, DIMENSION(:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE COMPLEX, DIMENSION(:,:,:,:), OPTIONAL :: ppformz
 !
     INTEGER :: d1, d2, k1, k2, n1, n2, np
     DOUBLE COMPLEX, ALLOCATABLE :: work(:,:,:), temp(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp))
     INTEGER :: leftx(SIZE(xp)), lefty(SIZE(yp))
     INTEGER :: i, j, k, ii, jj
     LOGICAL :: nlppform
 !
     d1 = sp%sp1%dim
     d2 = sp%sp2%dim
     k1 = sp%sp1%order
     k2 = sp%sp2%order
     n1 = sp%sp1%nints
     n2 = sp%sp2%nints
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !   Compute PPFORM/BCOEFS if spline coefs are passed
 !
     IF( PRESENT(c)) THEN
        IF( nlppform ) THEN
           ALLOCATE(work(d2,k1,n1))
           CALL topp1z(sp%sp1, c , work)
           IF(PRESENT(ppformz)) THEN
              CALL topp2z(sp%sp2, work, ppformz)
           ELSE
              IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE(sp%ppformz)
              ALLOCATE(sp%ppformz(k1,n1,k2,n2))
              CALL topp2z(sp%sp2, work, sp%ppformz)
           END IF
           DEALLOCATE(work)
        ELSE
           IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
           ALLOCATE(sp%bcoefsc(SIZE(c,1),SIZE(c,2)))
           sp%bcoefsc = c
        END IF
     END IF
 !
 !   Applly periodicity if required
 !
     np = SIZE(xp)
     IF( sp%sp1%period ) THEN   !  ** Applly periodicity **
        x = sp%sp1%knots(0) + MODULO(xp-sp%sp1%knots(0), sp%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp%sp2%period ) THEN   !  ** Applly periodicity **
        y = sp%sp2%knots(0) + MODULO(yp-sp%sp2%knots(0), sp%sp2%lperiod)
     ELSE
        y = yp
     END IF
 !
 !  Locate the interval containing x, y
 !
     CALL locintv(sp%sp1, x, leftx)
     CALL locintv(sp%sp2, y, lefty)
 !
 !  Compute function/derivatives
 !
     IF( nlppform ) THEN   ! using PP form
        ALLOCATE(temp(k2))
        DO i=1,np
           IF(PRESENT(ppformz)) THEN
              CALL ppval(sp%sp1, x(i), ppformz(:,leftx(i)+1,:,lefty(i)+1),&
                   &  leftx(i), jder(1), temp)
           ELSE
              CALL ppval(sp%sp1, x(i), sp%ppformz(:,leftx(i)+1,:,lefty(i)+1),&
                   &  leftx(i), jder(1), temp)
           END IF
           CALL ppval(sp%sp2, y(i), temp, lefty(i), jder(2), fp(i))
        END DO
        DEALLOCATE(temp)
     ELSE                   ! using spline expansion
        ALLOCATE(funx(1:k1,0:jder(1)), funy(1:k2,0:jder(2)))
        fp = (0.0d0,0.0d0)
        DO i=1,np
           CALL basfun(x(i), sp%sp1, funx, leftx(i)+1)
           CALL basfun(y(i), sp%sp2, funy, lefty(i)+1)
           DO jj=1,k2
              DO ii=1,k1
                 fp(i) = fp(i) + &
                    &   funy(jj,jder(2))*sp%bcoefsc(leftx(i)+ii,lefty(i)+jj)* &
                    &   funx(ii,jder(1))
              END DO
           END DO
        END DO
        DEALLOCATE(funx, funy)
     END IF
   END SUBROUTINE gridval2d_1dz
 !===========================================================================
   SUBROUTINE gridval2d_2dz(sp, xp, yp, fp, jder, c, ppformz)
 !
 !  Compute values or jder-th dervivative of f(x,y) from ppform 
 !  of spline sp. Recompute the ppform if the optional spline 
 !  coefficients c are given.
 !
 !              F(I,J) = F(X(I), Y(J))
 !
     TYPE(spline2d), INTENT(inout) :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in) :: xp, yp
     DOUBLE COMPLEX, DIMENSION(:,:), INTENT(out) :: fp
     INTEGER, INTENT(in) :: jder(2)
     DOUBLE COMPLEX, DIMENSION(:,:), OPTIONAL, INTENT(in) :: c
     DOUBLE COMPLEX, DIMENSION(:,:,:,:), OPTIONAL :: ppformz
 !
     INTEGER :: d1, d2, k1, k2, n1, n2
     DOUBLE COMPLEX, ALLOCATABLE :: work(:,:,:), temp(:)
     DOUBLE PRECISION, ALLOCATABLE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp))
     INTEGER :: leftx(SIZE(xp)), lefty(SIZE(yp))
     INTEGER :: i, j, k, ii, jj
     LOGICAL :: nlppform
 !
     d1 = sp%sp1%dim
     d2 = sp%sp2%dim
     k1 = sp%sp1%order
     k2 = sp%sp2%order
     n1 = sp%sp1%nints
     n2 = sp%sp2%nints
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !   Compute PPFORMM/BCOEFS if spline coefs are passed
 !
     IF( PRESENT(c) ) THEN
        IF( nlppform ) THEN
           ALLOCATE(work(d2,k1,n1))
           CALL topp1z(sp%sp1, c , work)
           IF(PRESENT(ppformz)) THEN
              CALL topp2z(sp%sp2, work, ppformz)
           ELSE
              IF( ASSOCIATED(sp%ppformz) ) DEALLOCATE(sp%ppformz)
              ALLOCATE(sp%ppformz(k1,n1,k2,n2))
              CALL topp2z(sp%sp2, work, sp%ppformz)
           END IF
           DEALLOCATE(work)
        ELSE
           IF( ASSOCIATED(sp%bcoefsc) ) DEALLOCATE(sp%bcoefsc)
           ALLOCATE(sp%bcoefsc(SIZE(c,1),SIZE(c,2)))
           sp%bcoefsc = c
        END IF
     END IF
 !
 !   Applly periodicity if required
 !
     IF( sp%sp1%period ) THEN   !  ** Applly periodicity **
        x = sp%sp1%knots(0) + MODULO(xp-sp%sp1%knots(0), sp%sp1%lperiod)
     ELSE
        x = xp
     END IF
     IF( sp%sp2%period ) THEN   !  ** Applly periodicity **
        y = sp%sp2%knots(0) + MODULO(yp-sp%sp2%knots(0), sp%sp2%lperiod)
     ELSE
        y = yp
     END IF
 !
 !   Locate interval containing (x,y)
 !
     CALL locintv(sp%sp1, x, leftx)
     CALL locintv(sp%sp2, y, lefty)
 !
 !  Compute function/derivatives
 !
     IF( nlppform ) THEN   ! using PP form
        ALLOCATE(temp(k2))
        DO j=1,SIZE(y)
           DO i=1,SIZE(x)
              IF(PRESENT(ppformz)) THEN
                 CALL ppval(sp%sp1, x(i), ppformz(:,leftx(i)+1,:,lefty(j)+1),&
                      &     leftx(i), jder(1), temp)
              ELSE
                 CALL ppval(sp%sp1, x(i), sp%ppformz(:,leftx(i)+1,:,lefty(j)+1),&
                      &     leftx(i), jder(1), temp)
              END IF
              CALL ppval(sp%sp2, y(j), temp, lefty(j), jder(2), fp(i,j))
           END DO
        END DO
        DEALLOCATE(temp)
     ELSE                   ! using spline expansion
        ALLOCATE(funx(1:k1,0:jder(1)), funy(1:k2,0:jder(2)))
        fp = 0.0d0
        DO j=1,SIZE(y)
           CALL basfun(y(j), sp%sp2, funy, lefty(j)+1)
           DO i=1,SIZE(x)
              CALL basfun(x(i), sp%sp1, funx, leftx(i)+1)
              DO jj=1,k2
                 DO ii=1,k1
                    fp(i,j) = fp(i,j) + sp%bcoefsc(leftx(i)+ii,lefty(j)+jj) * &
                         &   funx(ii,jder(1))*funy(jj,jder(2))
                 END DO
              END DO
           END DO
        END DO
        DEALLOCATE(funx, funy)
     END IF
   END SUBROUTINE gridval2d_2dz
 !===========================================================================
   SUBROUTINE calc_fftmass(spl, fftmat)
 !
 !  Compute FT of mass matrix for periodic spline on equidistant mesh
 !
     TYPE(spline1d)                :: spl
     DOUBLE PRECISION, INTENT(out) :: fftmat(0:)
 !
     INTEGER :: dim, nx, nidbas, ngauss
     DOUBLE PRECISION, ALLOCATABLE :: xgauss(:), wgauss(:)
     DOUBLE COMPLEX, ALLOCATABLE   :: ft_fun(:,:)
     INTEGER :: igauss, intv
 !
     CALL get_dim(spl, dim, nx, nidbas)
     CALL get_gauss(spl, ngauss)
     ALLOCATE(xgauss(ngauss), wgauss(ngauss))
     ALLOCATE(ft_fun(0:nx-1,1))
 !
 !  Integrate on first interval
     intv = 1
     CALL get_gauss(spl, ngauss, intv, xgauss, wgauss)
     fftmat = 0.0d0
     DO igauss=1,ngauss
        CALL ft_basfun(xgauss(igauss), spl, ft_fun, intv)
        fftmat(:) = fftmat(:) + wgauss(igauss)*ft_fun(:,1)*CONJG(ft_fun(:,1))
     END DO
 !
     DEALLOCATE(ft_fun)
     DEALLOCATE(xgauss, wgauss)
   END SUBROUTINE calc_fftmass
 !===========================================================================
   SUBROUTINE calc_fftmass_old(spl, fftmat)
 !
 !  Compute FT of mass matrix for periodic spline on equidistant mesh
 !
     TYPE(spline1d) :: spl
     DOUBLE PRECISION, INTENT(out) :: fftmat(0:)
     INTEGER :: dim, nx, nidbas, ngauss
     DOUBLE PRECISION, ALLOCATABLE :: fun(:,:), xgauss(:), wgauss(:), arow(:)
     INTEGER :: i, j, k, igauss, intv
     DOUBLE PRECISION :: pi, arg0, arg
 !
     CALL get_dim(spl, dim, nx, nidbas)
     CALL get_gauss(spl, ngauss)
     ALLOCATE(fun(0:nidbas,1)) ! Spline
     ALLOCATE(xgauss(ngauss), wgauss(ngauss))
     ALLOCATE(arow(0:nidbas))
 !
 !  Assemble the first row of the upper mass matrix
     arow = 0.0d0
     intv = 1   ! Get splines on Gauss points in first interval
     CALL get_gauss(spl, ngauss, intv, xgauss, wgauss)
     DO igauss=1,ngauss
        CALL basfun(xgauss(igauss), spl, fun, intv)
        DO i=0,nidbas
           DO j=0,nidbas-i
              arow(i)=arow(i)+fun(j,1)*fun(i+j,1)*wgauss(igauss)
           END DO
        END DO
     END DO
 !
 !  Fourier transform
     pi = 4.0d0*ATAN(1.0d0)
     arg0 = 2.0d0*pi/REAL(nx,8)
     DO k=0,nx-1
        fftmat(k) = arow(0)
        arg = k*arg0
        DO i=1,nidbas
           fftmat(k) = fftmat(k) + 2.0d0*arow(i)*COS(i*arg)
        END DO
     END DO
 !
     DEALLOCATE(arow)
     DEALLOCATE(fun)
     DEALLOCATE(xgauss, wgauss)
   END SUBROUTINE calc_fftmass_old
 !===========================================================================
   SUBROUTINE CompMassMatrix1(sp1, sp2, a, b, MassMatrix)
   
 ! Compute cross mass matrix MassMatrix between splines sp1 and sp2 over
 ! interval [a, b]
   
     IMPLICIT NONE
   
     TYPE(spline1d), INTENT(IN) :: sp1, sp2
     DOUBLE PRECISION, INTENT(IN) :: a, b
     DOUBLE PRECISION, DIMENSION(:, :), POINTER :: MassMatrix
     
     INTEGER :: ndim1, n1, nidbas1
     INTEGER :: ndim2, n2, nidbas2
     INTEGER :: nint, int, ngauss, ig, k1, k2, i1, j2, left1, left2
     DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: allknots, xg, wg 
     DOUBLE PRECISION, DIMENSION(:, :), ALLOCATABLE :: fun1, fun2
   
 !===========================================================================
 !              1.0 Prologue
 !
 !   Properties of spline space
 !
     CALL get_dim(sp1, ndim1, n1, nidbas1)
     CALL get_dim(sp2, ndim2, n2, nidbas2)
     
 !    PRINT "('In CompMassMatrix1')"
 !    PRINT "('sp1: dim, #intervals, degree', I, I, I)", ndim1, n1, nidbas1 
 !    PRINT "('sp2: dim, #intervals, degree', I, I, I)", ndim2, n2, nidbas2 
     
     ALLOCATE(fun1(0:nidbas1, 1)) ! needs only basis functions (no derivatives)
     ALLOCATE(fun2(0:nidbas2, 1)) ! needs only basis functions (no derivatives)
   
     IF (sp1%period) THEN
        IF (sp2%period) THEN
           ALLOCATE(MassMatrix(n1, n2))
        ELSE
           ALLOCATE(MassMatrix(n1, ndim2))
        END IF
     ELSE
        IF (sp2%period) THEN
           ALLOCATE(MassMatrix(ndim1, n2))
        ELSE
           ALLOCATE(MassMatrix(ndim1, ndim2))
        END IF
     END IF
   
 !
 !   Gauss quadature
 !
     ALLOCATE(allknots(0:n1+n2+3))
     CALL sorted_merge(sp1%knots(0:n1), n1+1, sp2%knots(0:n2), n2+1, a, b, allknots, nint)
     nint = nint-1
   
     ngauss = CEILING(REAL(nidbas1 + nidbas2 + 1, 8)/2.D0)
     ALLOCATE(xg(ngauss), wg(ngauss))
 !===========================================================================
 !              2.0 Assembly loop
 !
     MassMatrix = 0.d0
     DO int = 1, nint
        ! Get gauss abscissas and weights for current interval
        CALL gauleg(allknots(int-1), allknots(int), xg, wg, ngauss)
        DO ig = 1, ngauss
           CALL locintv(sp1, xg(ig), left1)
           CALL locintv(sp2, xg(ig), left2)
           CALL basfun(xg(ig), sp1, fun1, left1+1)
           CALL basfun(xg(ig), sp2, fun2, left2+1)
           DO k1 = 0, nidbas1
              IF (sp1%period) THEN
                 i1 = modulo(left1+1 + k1 -1, n1) +1
              ELSE
                 i1 = left1+1 + k1
              END IF
              
              DO k2 = 0, nidbas2
                 IF (sp2%period) THEN
                    j2 = modulo(left2+1 + k2 -1, n2) +1
                 ELSE
                    j2 = left2+1 + k2
                 END IF
                 
                 MassMatrix(i1, j2) = MassMatrix(i1, j2) + wg(ig)*fun1(k1, 1)*fun2(k2, 1)
              END DO
           END DO
        END DO
     END DO
   
 !===========================================================================
 !              3.0  Epilogue
 !
     DEALLOCATE(xg, wg)
     DEALLOCATE(fun1, fun2)
     DEALLOCATE(allknots)
     
   END SUBROUTINE CompMassMatrix1
 !===========================================================================
   SUBROUTINE CompMassMatrix_gb(sp1, sp2, a, b, MassMatrix)
   
 ! Compute cross mass matrix MassMatrix between splines sp1 and sp2 over
 ! interval [a, b]
   
     IMPLICIT NONE
   
     TYPE(spline1d), INTENT(IN)   :: sp1, sp2
     DOUBLE PRECISION, INTENT(IN) :: a, b
     TYPE(gbmat)                  :: MassMatrix
     
     INTEGER :: ndim1, n1, nidbas1
     INTEGER :: ndim2, n2, nidbas2
     INTEGER :: nint, int, ngauss, ig, k1, k2, i1, j2, left1, left2
     DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: allknots, xg, wg 
     DOUBLE PRECISION, DIMENSION(:, :), ALLOCATABLE :: fun1, fun2
     DOUBLE PRECISION :: val  
     !===========================================================================
 !              1.0 Prologue
 !
 !   Properties of spline space
 !
     CALL get_dim(sp1, ndim1, n1, nidbas1)
     CALL get_dim(sp2, ndim2, n2, nidbas2)
     ALLOCATE(fun1(0:nidbas1, 1)) ! needs only basis functions (no derivatives)
     ALLOCATE(fun2(0:nidbas2, 1)) ! needs only basis functions (no derivatives)
 !
 !   Gauss quadature
 !
     ALLOCATE(allknots(0:n1+n2+3))
     CALL sorted_merge(sp1%knots(0:n1), n1+1, sp2%knots(0:n2), n2+1, a, b, allknots, nint)
     nint = nint-1
   
     ngauss = CEILING(REAL(nidbas1 + nidbas2 + 1, 8)/2.D0)
     ALLOCATE(xg(ngauss), wg(ngauss))
 !===========================================================================
 !              2.0 Assembly loop
 !
     DO int = 1, nint
        ! Get gauss abscissas and weights for current interval
        CALL gauleg(allknots(int-1), allknots(int), xg, wg, ngauss)
        DO ig = 1, ngauss
           CALL locintv(sp1, xg(ig), left1)
           CALL locintv(sp2, xg(ig), left2)
           CALL basfun(xg(ig), sp1, fun1, left1+1)
           CALL basfun(xg(ig), sp2, fun2, left2+1)
           DO k1 = 0, nidbas1
              IF (sp1%period) THEN
                 i1 = modulo(left1+1 + k1 -1, n1) +1
              ELSE
                 i1 = left1+1 + k1
              END IF
              
              DO k2 = 0, nidbas2
                 IF (sp2%period) THEN
                    j2 = modulo(left2+1 + k2 -1, n2) +1
                 ELSE
                    j2 = left2+1 + k2
                 END IF
                 val = wg(ig)*fun1(k1, 1)*fun2(k2, 1)
                 CALL updtmat(MassMatrix, i1, j2, val)
              END DO
           END DO
        END DO
     END DO
   
 !===========================================================================
 !              3.0  Epilogue
 !
     DEALLOCATE(xg, wg)
     DEALLOCATE(fun1, fun2)
     DEALLOCATE(allknots)
     
   END SUBROUTINE CompMassMatrix_gb
 !===========================================================================
   SUBROUTINE CompMassMatrix_zgb(sp1, sp2, a, b, MassMatrix)
   
 ! Compute cross mass matrix MassMatrix between splines sp1 and sp2 over
 ! interval [a, b]
   
     IMPLICIT NONE
   
     TYPE(spline1d), INTENT(IN)   :: sp1, sp2
     DOUBLE PRECISION, INTENT(IN) :: a, b
     TYPE(zgbmat)                 :: MassMatrix
     
     INTEGER :: ndim1, n1, nidbas1
     INTEGER :: ndim2, n2, nidbas2
     INTEGER :: nint, int, ngauss, ig, k1, k2, i1, j2, left1, left2
     DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: allknots, xg, wg 
     DOUBLE PRECISION, DIMENSION(:, :), ALLOCATABLE :: fun1, fun2
     DOUBLE COMPLEX :: val  
 !===========================================================================
 !              1.0 Prologue
 !
 !   Properties of spline space
 !
     CALL get_dim(sp1, ndim1, n1, nidbas1)
     CALL get_dim(sp2, ndim2, n2, nidbas2)
     ALLOCATE(fun1(0:nidbas1, 1)) ! needs only basis functions (no derivatives)
     ALLOCATE(fun2(0:nidbas2, 1)) ! needs only basis functions (no derivatives)
 !
 !   Gauss quadature
 !
     ALLOCATE(allknots(0:n1+n2+3))
     CALL sorted_merge(sp1%knots(0:n1), n1+1, sp2%knots(0:n2), n2+1, a, b, allknots, nint)
     nint = nint-1
   
     ngauss = CEILING(REAL(nidbas1 + nidbas2 + 1, 8)/2.D0)
     ALLOCATE(xg(ngauss), wg(ngauss))
 !===========================================================================
 !              2.0 Assembly loop
 !
     DO int = 1, nint
        ! Get gauss abscissas and weights for current interval
        CALL gauleg(allknots(int-1), allknots(int), xg, wg, ngauss)
        DO ig = 1, ngauss
           CALL locintv(sp1, xg(ig), left1)
           CALL locintv(sp2, xg(ig), left2)
           CALL basfun(xg(ig), sp1, fun1, left1+1)
           CALL basfun(xg(ig), sp2, fun2, left2+1)
           DO k1 = 0, nidbas1
              IF (sp1%period) THEN
                 i1 = modulo(left1+1 + k1 -1, n1) +1
              ELSE
                 i1 = left1+1 + k1
              END IF
              
              DO k2 = 0, nidbas2
                 IF (sp2%period) THEN
                    j2 = modulo(left2+1 + k2 -1, n2) +1
                 ELSE
                    j2 = left2+1 + k2
                 END IF
                 val = wg(ig)*fun1(k1, 1)*fun2(k2, 1)
                 CALL updtmat(MassMatrix, i1, j2, val)
              END DO
           END DO
        END DO
     END DO
   
 !===========================================================================
 !              3.0  Epilogue
 !
     DEALLOCATE(xg, wg)
     DEALLOCATE(fun1, fun2)
     DEALLOCATE(allknots)
     
   END SUBROUTINE CompMassMatrix_zgb
 !===========================================================================  
   SUBROUTINE sorted_merge(arr1, n1, arr2, n2, a, b, arrm, nm) 
   
     IMPLICIT NONE
   
 ! Peforms:
 ! 1) Merge of arrays arr1 & arr2 including boundary values a & b
 ! 2) Sorts the merged arrays keeping only values in [a, b]
 ! 3) Removes duplicates
   
     INTEGER, INTENT(IN)  :: n1, n2
     INTEGER, INTENT(OUT) :: nm
     
     DOUBLE PRECISION, INTENT(IN) :: a, b
     DOUBLE PRECISION, INTENT(IN) :: arr1(n1), arr2(n2)
     DOUBLE PRECISION, DIMENSION(*), INTENT(OUT) :: arrm
   
     INTEGER :: i, j
   
 ! Merge the two arrays including a & b
     nm = n1 + n2 + 2
     arrm(1:nm) = (/ a, arr1(1:n1), b, arr2(1:n2) /)
   
 ! Sort
     CALL sort(arrm, nm)
   
 ! Remove duplicates 
     j = 1
     DO i = 2, nm
        IF(arrm(i) .GT. arrm(j)) THEN
           j = j + 1
           arrm(j) = arrm(i)
        END IF
     END DO
     nm = j
   
 ! Remove values outside [a, b]
     j = 0
     DO i = 1, nm
        IF((arrm(i) .GE. a) .AND. (arrm(i) .LE. b)) THEN
           j = j + 1
           arrm(j) = arrm(i)
        END IF
     END DO
     nm = j
   
   END SUBROUTINE sorted_merge
 !===========================================================================
   SUBROUTINE sort(arr, n)
   
 ! Sorts array ARR of length N into ascending numerical order by the 
 ! Shell-Mezgar algorithm. 
 ! See Sec. 8.1 of Numerical Recipes
   
     IMPLICIT NONE
     
     INTEGER, INTENT(IN) :: n 
     DOUBLE PRECISION, DIMENSION(n), INTENT(INOUT) :: arr
    
     INTEGER :: nsort, is, i, j, l, m
     DOUBLE PRECISION :: tmp
     DOUBLE PRECISION, PARAMETER :: tiny = 1D-5
     
     nsort = INT(LOG(REAL(n, 8))/LOG(2.D0) + tiny)
   
     m = n
     DO is = 1, nsort
        m = m/2
        DO j = 1, n-m
           i = j
           DO 
              l = i+m
              IF (arr(l) .LT. arr(i)) THEN
                 tmp = arr(i)
                 arr(i) = arr(l)
                 arr(l) = tmp
                 i = i-m
                 IF (i .LT. 1) EXIT
              ELSE
                 EXIT
              END IF
           END DO
        END DO
     END DO
   
   END SUBROUTINE sort
 !===========================================================================
   LOGICAL FUNCTION is_equid(x, dev)
 !
 !   Check whether mesh is euidistant or not
 !
     DOUBLE PRECISION, INTENT(in)            :: x(0:)
     DOUBLE PRECISION, INTENT(out), OPTIONAL :: dev
 !
     DOUBLE PRECISION :: dx(SIZE(x)-1), dxmin, dxmax, dxaver, e
     DOUBLE PRECISION, PARAMETER :: tol=1.d-6
     INTEGER :: n, i
     n=SIZE(x)-1
     dx = (/ (x(i)-x(i-1),i=1,n) /)
     dxmin = MINVAL(dx)
     dxmax = MAXVAL(dx)
     dxaver = (x(n)-x(0))/REAL(n,8)
     e = (dxmax-dxmin)/dxaver
 !!$    e = (dxmax-dxmin)/(SUM(x)/REAL(n+1))
     is_equid = e.LT.tol
     IF(PRESENT(dev)) dev = e
   END FUNCTION is_equid
 !===========================================================================
   SUBROUTINE create_fine(cmesh, h, fmap)
 !
 !   Create a fine mesh from a coarse mesh and returns its mapping 
 !
     DOUBLE PRECISION, INTENT(in)  :: cmesh(0:)
     DOUBLE PRECISION, INTENT(out) :: h
     INTEGER, POINTER, INTENT(out) :: fmap(:)
 !
     DOUBLE PRECISION, ALLOCATABLE :: fmesh(:)
     DOUBLE PRECISION :: xlen, hmin
     INTEGER :: n, nfine, i, ic
 !
     n = SIZE(cmesh)-1
     xlen = cmesh(n)-cmesh(0)
 !
 !     Minimum interval size
     hmin = xlen
     DO i=1,n
        hmin = MIN(hmin, cmesh(i)-cmesh(i-1))
     END DO
 !
 !     Create the fine mesh
     nfine = CEILING(xlen/hmin) 
     h = xlen / REAL(nfine,8)
     ALLOCATE(fmap(0:nfine))
     ALLOCATE(fmesh(0:nfine))
     fmesh = cmesh(0) + (/ (i*h, i=0,nfine) /)
     fmesh(nfine) = cmesh(n)
 !
 !    Map fine to coarse mesh
     ic = 0
     fmap(0) = ic
     DO i=1,nfine-1
        DO
           IF(fmesh(i).GE.cmesh(ic+1)) THEN 
              ic = ic+1
           ELSE
              EXIT
           END IF
        END DO
        fmap(i) = ic
     END DO
     fmap(nfine) = n-1
 !
     DEALLOCATE(fmesh)
   END SUBROUTINE create_fine
 !===========================================================================
   SUBROUTINE getgradr(sp, xp, yp, f00, f10, f01)
 !
 !   Compute the function f00 and its derivatives
 !     f10 = d/dx f
 !     f01 = d/dy f
 !   assuming that its PPFORM/BCOEFSC was already computed!
 !
     TYPE(spline2d), INTENT(inout)      :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in)     :: xp, yp
     DOUBLE PRECISION, DIMENSION(:), INTENT(out) :: f00, f10, f01
 !
     INTEGER :: np
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp))
     INTEGER :: leftx(SIZE(xp)), lefty(SIZE(yp))
     INTEGER :: i, ip, ii, jj, nidbas(2)
     DOUBLE PRECISION :: temp0(SIZE(xp),sp%sp2%order), temp1(SIZE(xp),sp%sp2%order)
     DOUBLE PRECISION, ALLOCATABLE, SAVE :: funx(:,:), funy(:,:)
     DOUBLE PRECISION, ALLOCATABLE, SAVE :: ftemp0(:), ftemp1(:)
     LOGICAL :: nlppform
 !
 !   Apply periodicity if required
 !
     np = SIZE(xp)
     nidbas(1) = sp%sp1%order-1
     nidbas(2) = sp%sp2%order-1
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !  Locate the interval containing x, y
 !
     CALL locintv(sp%sp1, xp, leftx)
     CALL locintv(sp%sp2, yp, lefty)
     x(:) = xp(:) - sp%sp1%knots(leftx(:))
     y(:) = yp(:) - sp%sp2%knots(lefty(:))
 !
 !  Compute function/derivatives
 !
     IF(nlppform) THEN
 !
 !    Using PPFORM
 !----------
        DO i=1,np
           CALL my_ppval1(nidbas(1), x(i), sp%ppform(:,leftx(i)+1,:,lefty(i)+1), &
                & temp0(i,:), temp1(i,:))
        END DO
 !
        CALL my_ppval0(nidbas(2), y, temp0, 0, f00)
        CALL my_ppval0(nidbas(2), y, temp0, 1, f01)
        CALL my_ppval0(nidbas(2), y, temp1, 0, f10)
     ELSE
 !
 !    Using spline expansion with sp%bcoefsc
 !----------
        IF(.NOT.ALLOCATED(funx)) THEN
           ALLOCATE(funx(0:nidbas(1),0:1))  ! Spline and its first derivative
           ALLOCATE(funy(0:nidbas(2),0:1))
           ALLOCATE(ftemp0(0:nidbas(1)))
           ALLOCATE(ftemp1(0:nidbas(1)))
        END IF
 !
        DO ip=1,np
           CALL my_splines(nidbas(1), x(ip), sp%sp1%val0(:,:,leftx(ip)+1), funx)
           CALL my_splines(nidbas(2), y(ip), sp%sp2%val0(:,:,lefty(ip)+1), funy)
           DO ii=0,nidbas(1)
              ftemp0(ii) = (0.d0,0.d0) 
              ftemp1(ii) = (0.d0,0.d0) 
              DO jj=0,nidbas(2)
                 ftemp0(ii) = ftemp0(ii) + sp%bcoefs(leftx(ip)+1+ii,lefty(ip)+1+jj)*funy(jj,0)
                 ftemp1(ii) = ftemp1(ii) + sp%bcoefs(leftx(ip)+1+ii,lefty(ip)+1+jj)*funy(jj,1)
              END DO
           END DO
           f00(ip) = SUM(funx(:,0)*ftemp0(:))
           f01(ip) = SUM(funx(:,0)*ftemp1(:))
           f10(ip) = SUM(funx(:,1)*ftemp0(:))
        END DO
 !-----------
     END IF
   CONTAINS
 !+++
     SUBROUTINE my_ppval0(p, x, ppform, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !   for many points x(:)
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x(:)
       DOUBLE PRECISION, INTENT(in) :: ppform(:,:)
       INTEGER, INTENT(in) :: jder
       DOUBLE PRECISION, INTENT(out) :: f(:)
       DOUBLE PRECISION :: fact
       INTEGER :: j
       SELECT CASE (jder)
       CASE(0)            ! function value
          SELECT CASE(p)
          CASE(1)
             f(:) = ppform(:,1) + x(:)*ppform(:,2)
          CASE(2)
             f(:) = ppform(:,1) + x(:)*(ppform(:,2)+x(:)*ppform(:,3))
 !!$         CASE(3)
 !!$            f(:) = ppform(:,1) + x(:)*(ppform(:,2)+x(:)*(ppform(:,3)+x(:)*ppform(:,4)))
          CASE(3:)
             f(:) = ppform(:,p+1)
             DO j=p,1,-1
                f(:) = f(:)*x(:) + ppform(:,j)
             END DO
          END SELECT
       CASE(1)            ! 1st derivative
          SELECT CASE(p)
          CASE(1)
             f(:) = ppform(:,2)
          CASE(2)
             f(:) = ppform(:,2) + x(:)*2.d0*ppform(:,3)
 !!$         CASE(3)
 !!$            f(:) = ppform(:,2) + x(:)*(2.d0*ppform(:,3)+x(:)*3.0d0*ppform(:,4))
          CASE(3:)
             f(:) = p*ppform(:,p+1)
             DO j=p-1,1,-1
                f(:) = f(:)*x(:) + j*ppform(:,j+1)
             END DO
          END SELECT
       CASE default       ! 2nd and higher derivatives
          f(:) = ppform(:,p+1)
          fact = p-jder
          DO j=p,jder+1,-1
             f(:) = f(:)/fact*j*x(:) + ppform(:,j)
             fact = fact-1.0d0
          END DO
          DO j=2,jder
             f(:) = f(:)*j
          END DO
       END SELECT
     END SUBROUTINE my_ppval0
 !+++
     SUBROUTINE my_ppval1(p, x, ppform, f0, f1)
 !
 !   Compute function and first derivative from the PP representation
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x
       DOUBLE PRECISION, INTENT(in) :: ppform(:,:)
       DOUBLE PRECISION, INTENT(out) :: f0(:)
       DOUBLE PRECISION, INTENT(out) :: f1(:)
       DOUBLE PRECISION :: fact
       INTEGER :: j
       SELECT CASE(p)
       CASE(1)
          f0(:) = ppform(1,:) + x*ppform(2,:)
          f1(:) = ppform(2,:)
       CASE(2)
          f0(:) = ppform(1,:) + x*(ppform(2,:)+x*ppform(3,:))
          f1(:) = ppform(2,:) + x*2.d0*ppform(3,:)
       CASE(3)
          f0(:) = ppform(1,:) + x*(ppform(2,:)+x*(ppform(3,:)+x*ppform(4,:)))
          f1(:) = ppform(2,:) + x*(2.d0*ppform(3,:)+x*3.0d0*ppform(4,:))
       CASE(4:)
          f0 = ppform(p+1,:)
          f1 = f0
          DO j=p,2,-1
             f0(:) = ppform(j,:) + x*f0(:)
             f1(:) = f0(:) + x*f1(:)
          END DO
          f0(:) = ppform(1,:) + x*f0(:)
       END SELECT
     END SUBROUTINE my_ppval1
 !+++
     SUBROUTINE my_splines(p, x, ppform, f)
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x
       DOUBLE PRECISION, INTENT(in) :: ppform(0:p,0:p)
       DOUBLE PRECISION, INTENT(out) :: f(0:p,0:1)
       INTEGER :: i
       DOUBLE PRECISION :: powerx(0:p)
       SELECT CASE(p)
       CASE(1)
          f(0,0) = ppform(0,0) + x*ppform(1,0)
          f(0,1) = ppform(1,0)
          f(1,0) = 1.0-f(0,0)
          f(1,1) = -f(0,1)
       CASE(2)
          f(0,0) = ppform(0,0) + x*(ppform(1,0)+x*ppform(2,0))
          f(0,1) = ppform(1,0) + 2.d0*x*ppform(2,0)
          f(1,0) = ppform(0,1) + x*(ppform(1,1)+x*ppform(2,1))
          f(1,1) = ppform(1,1) + 2.d0*x*ppform(2,1)
          f(2,0) = 1.0 - f(0,0) - f(1,0)
          f(2,1) = - f(0,1) - f(1,1)
       CASE(3)
          f(0,0) = ppform(0,0) + x*(ppform(1,0)+x*(ppform(2,0)+x*ppform(3,0)))
          f(0,1) = ppform(1,0) + x*(2.d0*ppform(2,0)+3.d0*x*ppform(3,0))
          f(1,0) = ppform(0,1) + x*(ppform(1,1)+x*(ppform(2,1)+x*ppform(3,1)))
          f(1,1) = ppform(1,1) + x*(2.d0*ppform(2,1)+3.d0*x*ppform(3,1))
          f(2,0) = ppform(0,2) + x*(ppform(1,2)+x*(ppform(2,2)+x*ppform(3,2)))
          f(2,1) = ppform(1,2) + x*(2.d0*ppform(2,2)+3.d0*x*ppform(3,2))
          f(3,0) = 1.0 - f(0,0) - f(1,0) - f(2,0)
          f(3,1) = - f(0,1) - f(1,1) - f(2,1)
       CASE(4:)
          powerx(0) = 1.d0
          DO i=1,p
             powerx(i) = powerx(i-1)*x
          END DO
          DO i=0,p-1
             f(i,0) = DOT_PRODUCT(ppform(:,i),powerx(:))
          END DO
          f(p,0) = 1.d0 - SUM(f(0:p-1,0))
          f(p,1) = - SUM(f(0:p-1,1))
       END SELECT
     END SUBROUTINE my_splines
 !+++
   END SUBROUTINE getgradr
 !===========================================================================
   SUBROUTINE getgradz(sp, xp, yp, f00, f10, f01)
 !
 !   Compute the function f00 and its derivatives
 !     f10 = d/dx f
 !     f01 = d/dy f
 !   assuming that its PPFORM/BCOEFSC was already computed!
 !
     TYPE(spline2d), INTENT(inout)      :: sp
     DOUBLE PRECISION, DIMENSION(:), INTENT(in)     :: xp, yp
     DOUBLE COMPLEX, DIMENSION(:), INTENT(out) :: f00, f10, f01
 !
     INTEGER :: np
     DOUBLE PRECISION :: x(SIZE(xp)), y(SIZE(yp))
     INTEGER :: leftx(SIZE(xp)), lefty(SIZE(yp))
     INTEGER :: i, ip, ii, jj, nidbas(2)
     DOUBLE COMPLEX :: temp0(SIZE(xp),sp%sp2%order), temp1(SIZE(xp),sp%sp2%order)
     DOUBLE PRECISION, ALLOCATABLE, SAVE :: funx(:,:), funy(:,:)
     DOUBLE COMPLEX, ALLOCATABLE, SAVE :: ftemp0(:), ftemp1(:)
     LOGICAL :: nlppform
 !
 !   Apply periodicity if required
 !
     np = SIZE(xp)
     nidbas(1) = sp%sp1%order-1
     nidbas(2) = sp%sp2%order-1
     nlppform = sp%sp1%nlppform .OR. sp%sp2%nlppform
 !
 !  Locate the interval containing x, y
 !
     CALL locintv(sp%sp1, xp, leftx)
     CALL locintv(sp%sp2, yp, lefty)
     x(:) = xp(:) - sp%sp1%knots(leftx(:))
     y(:) = yp(:) - sp%sp2%knots(lefty(:))
 !
 !  Compute function/derivatives
 !
     IF(nlppform) THEN
 !
 !    Using PPFORM
 !----------
        DO i=1,np
           CALL my_ppval1(nidbas(1), x(i), sp%ppformz(:,leftx(i)+1,:,lefty(i)+1), &
                & temp0(i,:), temp1(i,:))
        END DO
 !
        CALL my_ppval0(nidbas(2), y, temp0, 0, f00)
        CALL my_ppval0(nidbas(2), y, temp0, 1, f01)
        CALL my_ppval0(nidbas(2), y, temp1, 0, f10)
     ELSE
 !
 !    Using spline expansion with sp%bcoefsc
 !----------
        IF(.NOT.ALLOCATED(funx)) THEN
           ALLOCATE(funx(0:nidbas(1),0:1))  ! Spline and its first derivative
           ALLOCATE(funy(0:nidbas(2),0:1))
           ALLOCATE(ftemp0(0:nidbas(1)))
           ALLOCATE(ftemp1(0:nidbas(1)))
        END IF
 !
        DO ip=1,np
           CALL my_splines(nidbas(1), x(ip), sp%sp1%val0(:,:,leftx(ip)+1), funx)
           CALL my_splines(nidbas(2), y(ip), sp%sp2%val0(:,:,lefty(ip)+1), funy)
           DO ii=0,nidbas(1)
              ftemp0(ii) = (0.d0,0.d0) 
              ftemp1(ii) = (0.d0,0.d0) 
              DO jj=0,nidbas(2)
                 ftemp0(ii) = ftemp0(ii) + sp%bcoefsc(leftx(ip)+1+ii,lefty(ip)+1+jj)*funy(jj,0)
                 ftemp1(ii) = ftemp1(ii) + sp%bcoefsc(leftx(ip)+1+ii,lefty(ip)+1+jj)*funy(jj,1)
              END DO
           END DO
           f00(ip) = SUM(funx(:,0)*ftemp0(:))
           f01(ip) = SUM(funx(:,0)*ftemp1(:))
           f10(ip) = SUM(funx(:,1)*ftemp0(:))
        END DO
 !-----------
     END IF
   CONTAINS
 !+++
     SUBROUTINE my_ppval0(p, x, ppform, jder, f)
 !
 !   Compute function and derivatives from the PP representation
 !   for many points x(:)
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x(:)
       DOUBLE COMPLEX, INTENT(in) :: ppform(:,:)
       INTEGER, INTENT(in) :: jder
       DOUBLE COMPLEX, INTENT(out) :: f(:)
       DOUBLE PRECISION :: fact
       INTEGER :: j
       SELECT CASE (jder)
       CASE(0)            ! function value
          SELECT CASE(p)
          CASE(1)
             f(:) = ppform(:,1) + x(:)*ppform(:,2)
          CASE(2)
             f(:) = ppform(:,1) + x(:)*(ppform(:,2)+x(:)*ppform(:,3))
          CASE(3)
             f(:) = ppform(:,1) + x(:)*(ppform(:,2)+x(:)*(ppform(:,3)+x(:)*ppform(:,4)))
          CASE(4:)
             DO j=p+1,1,-1
                f(:) = f(:)*x(:) + ppform(:,j)
             END DO
          END SELECT
       CASE(1)            ! 1st derivative
          SELECT CASE(p)
          CASE(1)
             f(:) = ppform(:,2)
          CASE(2)
             f(:) = ppform(:,2) + x(:)*2.d0*ppform(:,3)
          CASE(3)
             f(:) = ppform(:,2) + x(:)*(2.d0*ppform(:,3)+x(:)*3.0d0*ppform(:,4))
          CASE(4:)
             DO j=p,1,-1
                f(:) = f(:)*x(:) + j*ppform(:,j+1)
             END DO
          END SELECT
       CASE default       ! 2nd and higher derivatives
          f(:) = ppform(:,p+1)
          fact = p-jder
          DO j=p,jder+1,-1
             f(:) = f(:)/fact*j*x(:) + ppform(:,j)
             fact = fact-1.0d0
          END DO
          DO j=2,jder
             f(:) = f(:)*j
          END DO
       END SELECT
     END SUBROUTINE my_ppval0
 !+++
     SUBROUTINE my_ppval1(p, x, ppform, f0, f1)
 !
 !   Compute function and first derivative from the PP representation
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x
       DOUBLE COMPLEX, INTENT(in) :: ppform(:,:)
       DOUBLE COMPLEX, INTENT(out) :: f0(:)
       DOUBLE COMPLEX, INTENT(out) :: f1(:)
       DOUBLE PRECISION :: fact
       INTEGER :: j
       SELECT CASE(p)
       CASE(1)
          f0(:) = ppform(1,:) + x*ppform(2,:)
          f1(:) = ppform(2,:)
       CASE(2)
          f0(:) = ppform(1,:) + x*(ppform(2,:)+x*ppform(3,:))
          f1(:) = ppform(2,:) + x*2.d0*ppform(3,:)
       CASE(3)
          f0(:) = ppform(1,:) + x*(ppform(2,:)+x*(ppform(3,:)+x*ppform(4,:)))
          f1(:) = ppform(2,:) + x*(2.d0*ppform(3,:)+x*3.0d0*ppform(4,:))
       CASE(4:)
          f0 = ppform(p+1,:)
          f1 = f0
          DO j=p,2,-1
             f0(:) = ppform(j,:) + x*f0(:)
             f1(:) = f0(:) + x*f1(:)
          END DO
          f0(:) = ppform(1,:) + x*f0(:)
       END SELECT
     END SUBROUTINE my_ppval1
 !+++
     SUBROUTINE my_splines(p, x, ppform, f)
       INTEGER, INTENT(in) :: p
       DOUBLE PRECISION, INTENT(in) :: x
       DOUBLE PRECISION, INTENT(in) :: ppform(0:p,0:p)
       DOUBLE PRECISION, INTENT(out) :: f(0:p,0:1)
       INTEGER :: i
       DOUBLE PRECISION :: powerx(0:p)
       SELECT CASE(p)
       CASE(1)
          f(0,0) = ppform(0,0) + x*ppform(1,0)
          f(0,1) = ppform(1,0)
          f(1,0) = 1.0-f(0,0)
          f(1,1) = -f(0,1)
       CASE(2)
          f(0,0) = ppform(0,0) + x*(ppform(1,0)+x*ppform(2,0))
          f(0,1) = ppform(1,0) + 2.d0*x*ppform(2,0)
          f(1,0) = ppform(0,1) + x*(ppform(1,1)+x*ppform(2,1))
          f(1,1) = ppform(1,1) + 2.d0*x*ppform(2,1)
          f(2,0) = 1.0 - f(0,0) - f(1,0)
          f(2,1) = - f(0,1) - f(1,1)
       CASE(3)
          f(0,0) = ppform(0,0) + x*(ppform(1,0)+x*(ppform(2,0)+x*ppform(3,0)))
          f(0,1) = ppform(1,0) + x*(2.d0*ppform(2,0)+3.d0*x*ppform(3,0))
          f(1,0) = ppform(0,1) + x*(ppform(1,1)+x*(ppform(2,1)+x*ppform(3,1)))
          f(1,1) = ppform(1,1) + x*(2.d0*ppform(2,1)+3.d0*x*ppform(3,1))
          f(2,0) = ppform(0,2) + x*(ppform(1,2)+x*(ppform(2,2)+x*ppform(3,2)))
          f(2,1) = ppform(1,2) + x*(2.d0*ppform(2,2)+3.d0*x*ppform(3,2))
          f(3,0) = 1.0 - f(0,0) - f(1,0) - f(2,0)
          f(3,1) = - f(0,1) - f(1,1) - f(2,1)
       CASE(4:)
          powerx(0) = 1.d0
          DO i=1,p
             powerx(i) = powerx(i-1)*x
          END DO
          DO i=0,p-1
             f(i,0) = DOT_PRODUCT(ppform(:,i),powerx(:))
          END DO
          f(p,0) = 1.d0 - SUM(f(0:p-1,0))
          f(p,1) = - SUM(f(0:p-1,1))
       END SELECT
     END SUBROUTINE my_splines
 !+++
   END SUBROUTINE getgradz
 END MODULE bsplines