PROGRAM splinalg
  use :: fgsl
  use, intrinsic :: iso_fortran_env, only : output_unit, error_unit
  implicit none
  integer(fgsl_size_t), parameter :: bigN = 100           !number of grid points
  integer(fgsl_size_t), parameter :: n = bigN - 2         !subtract 2 to exclude boundaries
  real(fgsl_double), parameter :: h = 1.0 / (bigN - 1.0)  !grid spacing
  type(fgsl_spmatrix) :: A                                !triplet format
  type(fgsl_spmatrix) :: C                                !compressed format
  type(fgsl_vector) :: f                                  !right hand side vector
  type(fgsl_vector) :: u                                  !solution vector
  integer(fgsl_size_t) ::  i
  integer(fgsl_int) :: status
  real(fgsl_double) :: fi, xi
  real(fgsl_double), parameter :: tol = 1.0e-6            !solution relative tolerance
  integer(fgsl_size_t), parameter :: max_iter = 10        !maximum iterations
  type(fgsl_splinalg_itersolve_type), parameter :: T = fgsl_splinalg_itersolve_gmres
  type(fgsl_splinalg_itersolve) :: work
  integer(fgsl_size_t) :: iter = 0
  real(fgsl_double) :: residual, u_gsl, u_exact
  real(fgsl_double),dimension(0:n-1),target :: f_f, u_f

  A = fgsl_spmatrix_alloc(n,n)
  f = fgsl_vector_init(f_f)
  u = fgsl_vector_init(u_f)
  work = fgsl_splinalg_itersolve_alloc(T, n, 0_fgsl_size_t)

  ! construct the sparse matrix for the finite difference equation

  ! construct first row
  status = fgsl_spmatrix_set(A, 0_fgsl_size_t, 0_fgsl_size_t, -2.0_fgsl_double)
  status = fgsl_spmatrix_set(A, 0_fgsl_size_t, 1_fgsl_size_t, 1.0_fgsl_double)

  ! construct rows [1:n-2]
  do i=1,n-2
    status = fgsl_spmatrix_set(A, i, i + 1, 1.0_fgsl_double)
    status = fgsl_spmatrix_set(A, i, i, -2.0_fgsl_double)
    status = fgsl_spmatrix_set(A, i, i - 1, 1.0_fgsl_double)
  end do

  ! construct last row
  status = fgsl_spmatrix_set(A, n - 1, n - 1, -2.0_fgsl_double)
  status = fgsl_spmatrix_set(A, n - 1, n - 2, 1.0_fgsl_double)

  ! scale by h^2
  status = fgsl_spmatrix_scale(A, 1.0 / (h * h))

  ! construct right hand side vector
  do i=0,n-1
    xi = (i + 1) * h
    fi = -M_PI * M_PI * sin(M_PI * xi)
    f_f(i) = fi
  end do

  ! convert to compressed column format
  C = fgsl_spmatrix_compcol(A)

  ! now solve the system with the GMRES iterative solver
  ! initial guess u = 0
  u_f = 0

  ! solve the system A u = f
  do
    status = fgsl_splinalg_itersolve_iterate(C, f, tol, u, work)

    ! print out residual norm ||A*u - f||
    residual = fgsl_splinalg_itersolve_normr(work);
    write(output_unit, '(A,I2,A,G15.6)') 'iter ', iter, ' residual = ', residual

    if (status == FGSL_SUCCESS) then
      write(output_unit, '(A)') 'Converged'
    endif
    iter = iter + 1
    if (status /= FGSL_CONTINUE .or. iter >= max_iter) exit
  end do
  ! output solution
  do i=0,n-1
    xi = (i + 1) * h
    u_exact = sin(M_PI * xi)
    u_gsl = u_f(i)

    !printf("%f %.12e %.12e\n", xi, u_gsl, u_exact)
    write(output_unit, '(3G15.6)') xi, u_gsl, u_exact
  end do

  call fgsl_splinalg_itersolve_free(work)

  call fgsl_spmatrix_free(A)
  call fgsl_spmatrix_free(C)
  call fgsl_vector_free(f)
  call fgsl_vector_free(u)
end program splinalg
