#include "config.h"
module mod_ode
  use fgsl
  use, intrinsic :: iso_c_binding
  implicit none
contains
  function func(t, y_c, dydt_c, params) bind(c)
    real(c_double), value :: t
    type(c_ptr), value :: y_c, dydt_c
    real(c_double), dimension(:), pointer :: y, dydt
    type(c_ptr), value :: params
    integer(c_int) :: func
!
    real(c_double), pointer :: mu
!
    call c_f_pointer(y_c, y, [2])
    call c_f_pointer(dydt_c, dydt, [2])
    call c_f_pointer(params, mu)
    dydt(1) = y(2)
    dydt(2) = -y(1) - mu*y(2)*(y(1)*y(1) - 1)
    func = fgsl_success
  end function func
  function jac(t, y_c, dfdy_c, dfdt_c, params) bind(c)
    real(c_double), value :: t
    type(c_ptr), value :: y_c, dfdy_c, dfdt_c
    real(c_double), dimension(:), pointer :: y, dfdy, dfdt
    type(c_ptr), value :: params
    integer(c_int) :: jac
!
    real(c_double), pointer :: mu
!
    call c_f_pointer(y_c, y, [2])
    call c_f_pointer(dfdy_c, dfdy, [4])
    call c_f_pointer(dfdt_c, dfdt, [2])
    call c_f_pointer(params, mu)
    dfdy(1) = 0.0_c_double
    dfdy(2) = 1.0_c_double
    dfdy(3) = -2.0_c_double*mu*y(1)*y(2) - 1.0_c_double
    dfdy(4) = -mu*(y(1)*y(1) - 1.0_c_double)
    dfdt(1:2) = 0.0_c_double
    jac = fgsl_success
  end function jac
end module mod_ode
program ode
  use mod_ode
  implicit none
  type(fgsl_odeiv2_system) :: ode_system
  type(fgsl_odeiv2_step) :: ode_step
  type(fgsl_odeiv2_control) :: ode_ctrl
  type(fgsl_odeiv2_evolve) :: ode_evlv
  type(c_ptr) :: ptr
  integer(fgsl_int) :: status
  real(fgsl_double) :: y(2), h, t, t1
  real(fgsl_double), target :: mu
  character(kind=fgsl_char, len=fgsl_strmax) :: name
! Note: bsimp uses the jacobian as opposed to rk8pd
  ode_step = fgsl_odeiv2_step_alloc(fgsl_odeiv2_step_bsimp,2_fgsl_size_t)
  name = fgsl_odeiv2_step_name(ode_step)
  ode_ctrl = fgsl_odeiv2_control_y_new(1.0e-6_fgsl_double, 0.0_fgsl_double)
  ode_evlv = fgsl_odeiv2_evolve_alloc(2_fgsl_size_t)
  mu = 10.0e0_c_double
  ptr = c_loc(mu)
  ode_system = fgsl_odeiv2_system_init(func, 2_c_size_t, ptr, jac)
!
  t = 0.0_fgsl_double; t1 = 100.0_fgsl_double
  h = 1.0e-6_fgsl_double
  y = (/1.0_c_double, 0.0_c_double /)
  write(6, '(''# Using the '',A,'' algorithm'')') trim(name)
  do while (t < t1)
     status = fgsl_odeiv2_evolve_apply(ode_evlv, ode_ctrl, ode_step, &
          ode_system, t, t1, h, y)
     if (status /= FGSL_SUCCESS) exit
     write(6,fmt='(3(1PE15.8,1X))') t, y(1), y(2)
  end do
  call fgsl_odeiv2_evolve_free (ode_evlv)
  call fgsl_odeiv2_control_free (ode_ctrl)
  call fgsl_odeiv2_step_free (ode_step)
  call fgsl_odeiv2_system_free(ode_system)
end program ode
