module qRKmod
  !developer => sikino
  !date : 2016/07/30 (yyyy/mm/dd)
  implicit none
  interface qrkf45
     module procedure &
          qrkf451, &
          zrkf451
  end interface qrkf45
 
  interface qrkf45_e
     module procedure &
          qrkf451_e, &
          zrkf451_e
  end interface qrkf45_e
  !For Butcher table
  integer,private::s
  real*16,private,allocatable::a(:,:),b1(:),b2(:),c(:),Rc(:)
  !-----------------------------
contains
  subroutine qrk_preparation(method)
    !set Butcher table
    implicit none
    character(*)::method
    if(trim(method).eq."rk4")then
       s=4
       allocate(a(1:s,1:s),b1(1:s),c(1:s))
       a=0q0; b1=0q0; c=0q0
       c(1:4)=(/0q0,0.5q0,0.5q0,1q0/)
       a(1,1:4)=(/0q0,0q0,0q0,0q0/)
       a(2,1:4)=(/0.5q0,0q0,0q0,0q0/)
       a(3,1:4)=(/0q0,0.5q0,0q0,0q0/)
       a(4,1:4)=(/0q0,0q0,1q0,0q0/)
       b1(1:4)=(/1q0/6q0, 1q0/3q0, 1q0/3q0, 1q0/6q0/)
    elseif(trim(method).eq."rkf45")then
       s=6
       allocate(a(1:s,1:s),b1(1:s),b2(1:s),c(1:s),Rc(1:s))
       a=0q0; b1=0q0; b2=0q0; c=0q0; Rc=0q0
       c(1:6)=(/0q0, 0.25q0, 3q0/8q0, 12q0/13q0, 1q0, 0.5q0/)
       a(1:6,1:6)=0q0
       a(1,1:6)=(/0q0, 0q0, 0q0, 0q0, 0q0, 0q0/)
       a(2,1:6)=(/0.25q0, 0q0, 0q0, 0q0, 0q0, 0q0/)
       a(3,1:6)=(/0.09375q0, 0.28125q0, 0q0, 0q0, 0q0, 0q0/)
       a(4,1:6)=(/1932q0/2197q0, -7200q0/2197q0, 7296q0/2197q0, 0q0, 0q0, 0q0/)
       a(5,1:6)=(/439q0/216q0, -8q0, 3680q0/513q0, -845q0/4104q0, 0q0, 0q0/)
       a(6,1:6)=(/-8q0/27q0, 2q0, -3544q0/2565q0, 1859q0/4104q0, -11q0/40q0, 0q0/)
       b2(1:6)=(/16q0/135q0, 0q0, 6656q0/12825q0, 28561q0/56430q0, -9q0/50q0, 2q0/55q0/)
       b1(1:6)=(/25q0/216q0, 0q0, 1408q0/2565q0, 2197q0/4104q0, -0.2q0, 0q0/)  
       Rc(1:6)=(/1q0/360q0,0q0,-128q0/4275q0,-2197q0/75240q0,1q0/50q0,2q0/55q0/)
    else
       write(6,*)"program stop at rk_preparation"
       stop
    end if
    return
  end subroutine qrk_preparation
  !------------------------------
  subroutine qrk_deallocation(method)
    implicit none
    character(*)::method
    if(trim(method).eq."rk4")then
       deallocate(a,b1,c)
    elseif(trim(method).eq."rkf45")then
       deallocate(a,b1,b2,c,Rc)
    else
       write(6,*)"program stop at rk_deallocation"
       stop
    end if
    return
  end subroutine qrk_deallocation
  !-------------------
  subroutine rk41_Nstep(func,Nstep,N,x,h,y)
    implicit none
    interface
       function func(iN,ix,iy,is)
         implicit none
         integer,intent(in)::iN,is
         real*16,intent(in)::ix,iy(1:iN)
         real*16::func
       end function func
    end interface
    integer,intent(in)::N,Nstep
    real*16,intent(inout)::x,h,y(1:N)
    integer::i,j,l
    real*16::tx,tmp(1:N),K(1:s,1:N)
   
    do l=1,Nstep
       tx=x
       do j=1,s
          tx=x+c(j)*h
          tmp(1:N)=y(1:N)
          do i=1,j-1
             tmp(1:N)=tmp(1:N)+K(i,1:N)*a(j,i)
          enddo
          do i=1,N
             K(j,i)=h*func(N,tx,tmp,i)
          enddo
       enddo
       x=x+h  
       do i=1,s
          y(1:N)=y(1:N)+b1(i)*K(i,1:N)
       enddo
    enddo
    return
  end subroutine rk41_Nstep
  !-------------------
  subroutine rk42_Nstep(func,Nstep,N,x,h,y,dy)
    implicit none
    interface
       function func(iN,ix,iy,idy,isig,is)
         implicit none
         integer,intent(in)::iN,is,isig
         real*16,intent(in)::ix,iy(1:iN),idy(1:iN)
         real*16::func
       end function func
    end interface
    integer,intent(in)::N,Nstep
    real*16,intent(inout)::x,h,y(1:N),dy(1:N)
    integer::i,j,l
    real*16::tx,tmp(1:N),tmp2(1:N),K(1:2,1:s,1:N)
   
    do l=1,Nstep
       tx=x
       do j=1,s
          tx=x+c(j)*h
          tmp(1:N)=y(1:N)
          tmp2(1:N)=dy(1:N)
          do i=1,j-1
             tmp(1:N)=tmp(1:N)+K(1,i,1:N)*a(j,i)
             tmp2(1:N)=tmp2(1:N)+K(2,i,1:N)*a(j,i)
          enddo
          do i=1,N
             K(1,j,i)=h*func(N,tx,tmp,tmp2,1,i)
             K(2,j,i)=h*func(N,tx,tmp,tmp2,2,i)
          enddo
       enddo
       x=x+h  
       do i=1,s
          y(1:N)=y(1:N)+b1(i)*K(1,i,1:N)
          dy(1:N)=dy(1:N)+b1(i)*K(2,i,1:N)
       enddo
    enddo
    return
  end subroutine rk42_Nstep
  !----------------
  subroutine qrkf451(func,x,h,y,xbound,info,tol,SS)
    !sikinote
    ! if h < hmin, propagate forcibly with warning.
    !
    !-----------------
    !info = -1  (maybe path the discontinue points)
    !     =  0  (Running now)
    !     =  1  (x reach xbound)
    !-----------------
    !
    !          xbound
    !         /
    ! SS(i) = |  func_i(x) dx
    !         /
    !         x
    !  
    implicit none
    interface
       function func(iN,ix,iy,is)
         implicit none
         integer,intent(in)::iN,is
         real*16,intent(in)::ix,iy(1:iN)
         real*16::func
       end function func
    end interface
    integer::N
    real*16,intent(in)::xbound
    real*16,intent(in)::tol
    real*16,intent(out),optional::SS(:)
    real*16,intent(inout)::x,h,y(:)
    integer,intent(inout)::info
    real*16,parameter::hmin=1q-25,hmax=1.q0
    integer::i,j,FLAG,key
    real*16::R,delta,tx,Sy,err
    real*16,allocatable::tmp(:),K(:,:)
    key=0
    N=size(y,1)
    allocate(tmp(1:N),K(1:s,1:N))
    tmp=0q0; K=0q0
    if(present(SS))SS(1:N)=0q0
   
    if(abs(h).ge.hmax)then
       h=sign(1q0,h)*hmax
    endif
    FLAG=1
    if(abs(x-xbound).le.hmin*0.1q0)then
       info=1
       FLAG=0
    endif
    do while(FLAG.eq.1)
       tx=x
       do j=1,s
          tx=x+c(j)*h
          tmp(1:N)=y(1:N)
          do i=1,j-1
             tmp(1:N)=tmp(1:N)+K(i,1:N)*a(j,i)
          enddo
          do i=1,N
             K(j,i)=h*func(N,tx,tmp,i)
          enddo
       enddo
       !step 4
       R=0q0
       do i=1,N
          R=R+(Rc(1)*K(1,i)+Rc(3)*K(3,i)+Rc(4)*K(4,i)+Rc(5)*K(5,i)+Rc(6)*K(6,i))**2
       enddo
       R=abs(sqrt(R)/h)
       Sy=0q0
       do i=1,N
          Sy=Sy+(y(i)*y(i))
       enddo
       Sy=sqrt(Sy)
       if(Sy.ge.1q0)then
          err=tol*Sy
       else
          err=tol
       endif
       !step 5
       if(R.le.err.or.key.eq.1)then
          if(present(SS))then
             SS(1:N)=0q0
             x=x+h
             do i=1,s
                SS(1:N)=SS(1:N)+b1(i)*K(i,1:N)
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          else
             x=x+h
             do i=1,s
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          endif
          FLAG=0
       endif
       !step 6
       !  Avoid zero deviding.
       if(R.ge.1q-40)then
          delta=(err/(2q0*R))**0.25q0
       else
          delta=4q0
       endif
       !step 7
       if(delta.le.0.1q0)then
          !function changes dramatically.
          h=0.1q0*h
       elseif(delta.ge.4q0)then
          !function changes loosely.
          h=4q0*h
       else
          !function changes moderately.
          h=delta*h
       endif
       !step 8
       if(abs(h).ge.hmax)then
          h=sign(1q0,h)*hmax
       elseif(abs(h).lt.hmin)then
          h=sign(1q0,h)*hmin
          key=1
       endif
       !step 9
       if(abs(xbound-x).le.abs(h))then
          h=xbound-x
          if(abs(h).le.hmin)then
             info=1
             FLAG=0
          endif
       end if
       if(h.le.0q0.and.xbound-x.ge.0q0)then
          info=1
          FLAG=0
       elseif(h.ge.0q0.and.xbound-x.le.0q0)then
          info=1
          FLAG=0
       endif
       !if(FLAG.eq.1)write(6,*)"mis,h",h
    enddo  
   
    if(key.eq.1)then
       write(6,'(A,f10.5,A,f10.5)')"Maybe path the singlar, discontinue points, or computation error between ",x-h," and ",x
       info=-1
    endif
   
    deallocate(tmp,K)
    return
  end subroutine qrkf451
  !-------------------
  subroutine qrkf451_e(func,x,y,xbound,info,tol,SS)
    !sikinote
    !  propagate from y(x) to y(xbound) without interval
    !
    !  info = -1 : h < hmin. Maybe path the singular point.
    !       =  1 : x reach xbound.
    !
    implicit none
    interface
       function func(iN,ix,iy,is)
         implicit none
         integer,intent(in)::iN,is
         real*16,intent(in)::ix,iy(1:iN)
         real*16::func
       end function func
    end interface
    integer::N
    real*16,intent(in)::xbound
    real*16,intent(in)::tol
    real*16,intent(out),optional::SS(:)
    real*16,intent(inout)::x,y(:)
    integer,intent(inout)::info
    real*16,parameter::hmin=1q-25,hmax=1.q0
    integer::i,j,FLAG,key,disc
    real*16::R,delta,tx,Sy,err,h,h0
    real*16,allocatable::tmp(:),K(:,:)
    disc=0
    key=0
    h0=999q0
    N=size(y,1)
    allocate(tmp(1:N),K(1:s,1:N))
    tmp=0q0; K=0q0  
    if(present(SS))SS(1:N)=0q0
   
    h=xbound-x
   
    if(abs(h).ge.hmax)then
       if(h.le.0q0)then
          h=-hmax
       else
          h=hmax
       endif
    endif
    FLAG=1
    if(abs(x-xbound).le.hmin*0.1q0)then
       info=1
       FLAG=0
    endif
    do while(FLAG.eq.1)
       tx=x
       do j=1,s
          tx=x+c(j)*h
          tmp(1:N)=y(1:N)
          do i=1,j-1
             tmp(1:N)=tmp(1:N)+K(i,1:N)*a(j,i)
          enddo
          do i=1,N
             K(j,i)=h*func(N,tx,tmp,i)
          enddo
       enddo
       !step 4
       R=0q0
       do i=1,N
          R=R+(Rc(1)*K(1,i)+Rc(3)*K(3,i)+Rc(4)*K(4,i)+Rc(5)*K(5,i)+Rc(6)*K(6,i))**2
       enddo
       R=abs(sqrt(R)/h)
       Sy=0q0
       do i=1,N
          Sy=Sy+(y(i)*y(i))
       enddo
       Sy=sqrt(Sy)
       if(Sy.ge.1q0)then
          err=tol*Sy
       else
          err=tol
       endif
       !step 5
       if(R.le.err.or.key.eq.1)then
          if(present(SS))then
             x=x+h
             do i=1,s
                SS(1:N)=SS(1:N)+b1(i)*K(i,1:N)
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          else
             x=x+h
             do i=1,s
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          endif
          key=0
       endif
       !step 6
       !  Avoid zero deviding.
       if(R.ge.1q-40)then
          delta=(err/(2q0*R))**0.25q0
       else
          delta=4q0
       endif
       !step 7
       if(delta.le.0.1q0)then
          !function changes dramatically.
          h=0.1q0*h
       elseif(delta.ge.4q0)then
          !function changes loosely.
          h=4q0*h
       else
          !function changes moderately.
          h=delta*h
       endif
       !step 8
       if(abs(h).ge.hmax)then
          h=sign(1q0,h)*hmax
       elseif(abs(h).lt.hmin)then
          h=sign(1q0,h)*hmin
          key=1
          disc=1
       endif
       
       !step 9
       if(abs(xbound-x).le.abs(h))then
          h=xbound-x
          if(abs(h).le.hmin)then
             info=1
             FLAG=0
          endif
       end if
    enddo
   
    if(disc.eq.1)then
       info=-1
    endif
       
    deallocate(tmp,K)
    return
  end subroutine qrkf451_e
  !-----------------------
  subroutine zrkf451(func,x,h,y,xbound,info,tol,SS)
    !sikinote
    ! if h < hmin, propagate forcibly with warning.
    !
    !-----------------
    !info = -1  (maybe path the discontinue points)
    !     =  0  (Running now)
    !     =  1  (x reach xbound)
    !-----------------
    !
    !          xbound
    !         /
    ! SS(i) = |  func_i(x) dx
    !         /
    !         x
    !  
    implicit none
    interface
       function func(iN,ix,iy,is)
         implicit none
         integer,intent(in)::iN,is
         real*16,intent(in)::ix
         complex*32,intent(in)::iy(1:iN)
         complex*32::func
       end function func
    end interface
    integer::N
    real*16,intent(in)::xbound
    real*16,intent(in)::tol
    complex*32,intent(out),optional::SS(:)
    real*16,intent(inout)::x,h
    complex*32,intent(inout)::y(:)
    integer,intent(inout)::info
    real*16,parameter::hmin=1q-25,hmax=1.q0
    integer::i,j,FLAG,key
    real*16::R,delta,tx,Sy,err
    complex*32,allocatable::tmp(:),K(:,:)
    key=0
    N=size(y,1)
    allocate(tmp(1:N),K(1:s,1:N))
    tmp=cmplx(0q0,0q0,kind=16); K=cmplx(0q0,0q0,kind=16)
    if(present(SS))SS(1:N)=cmplx(0q0,0q0,kind=16)
   
    if(abs(h).ge.hmax)then
       h=sign(1q0,h)*hmax
    endif
    FLAG=1
    if(abs(x-xbound).le.hmin*0.1q0)then
       info=1
       FLAG=0
    endif
    do while(FLAG.eq.1)
       tx=x
       do j=1,s
          tx=x+c(j)*h
          tmp(1:N)=y(1:N)
          do i=1,j-1
             tmp(1:N)=tmp(1:N)+K(i,1:N)*a(j,i)
          enddo
          do i=1,N
             K(j,i)=h*func(N,tx,tmp,i)
          enddo
       enddo
       !step 4
       R=0q0
       do i=1,N
          R=R+abs(Rc(1)*K(1,i)+Rc(3)*K(3,i)+Rc(4)*K(4,i)+Rc(5)*K(5,i)+Rc(6)*K(6,i))**2
       enddo
       R=sqrt(R)/h
       Sy=0q0
       do i=1,N
          Sy=Sy+abs(y(i))**2
       enddo
       Sy=sqrt(Sy)
       if(Sy.ge.1q0)then
          err=tol*Sy
       else
          err=tol
       endif
       !step 5
       if(R.le.err.or.key.eq.1)then
          if(present(SS))then
             x=x+h
             do i=1,s
                SS(1:N)=SS(1:N)+b1(i)*K(i,1:N)
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          else
             x=x+h
             do i=1,s
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          endif
          FLAG=0
       endif
       !step 6
       !  Avoid zero deviding.
       if(R.ge.1q-40)then
          delta=(err/(2q0*R))**0.25q0
       else
          delta=4q0
       endif
       !step 7
       if(delta.le.0.1q0)then
          !function changes dramatically.
          h=0.1q0*h
       elseif(delta.ge.4q0)then
          !function changes loosely.
          h=4q0*h
       else
          !function changes moderately.
          h=delta*h
       endif
       !step 8
       if(abs(h).ge.hmax)then
          h=sign(1q0,h)*hmax
       elseif(abs(h).lt.hmin)then
          h=sign(1q0,h)*hmin
          key=1
       endif
       !step 9
       if(abs(xbound-x).le.abs(h))then
          h=xbound-x
          if(abs(h).le.hmin)then
             info=1
             FLAG=0
          endif
       end if
       if(h.le.0q0.and.xbound-x.ge.0q0)then
          info=1
          FLAG=0
       elseif(h.ge.0q0.and.xbound-x.le.0q0)then
          info=1
          FLAG=0
       endif
    enddo  
   
    if(key.eq.1)then
       write(6,'(A,f10.5,A,f10.5)')"Maybe path the singlar, discontinue points, or computation error between ",x-h," and ",x
       info=-1
    endif
   
    deallocate(tmp,K)
    return
  end subroutine zrkf451
  !-------------------
  subroutine zrkf451_e(func,x,y,xbound,info,tol,SS)
    !sikinote
    !  propagate from y(x) to y(xbound) without interval
    !
    !  info = -1 : h < hmin. Maybe path the singular point.
    !       =  1 : x reach xbound.
    !
    implicit none
    interface
       function func(iN,ix,iy,is)
         implicit none
         integer,intent(in)::iN,is
         real*16,intent(in)::ix
         complex*32,intent(in)::iy(1:iN)
         complex*32::func
       end function func
    end interface
    integer::N
    real*16,intent(in)::xbound
    real*16,intent(in)::tol
    complex*32,intent(out),optional::SS(:)
    real*16,intent(inout)::x
    complex*32,intent(inout)::y(:)
    integer,intent(inout)::info
    real*16,parameter::hmin=1q-25,hmax=1.q0
    integer::i,j,FLAG,key,disc
    real*16::R,delta,tx,Sy,err,h,h0
    complex*32,allocatable::tmp(:),K(:,:)
    disc=0
    key=0
    h0=999q0
    N=size(y,1)
    allocate(tmp(1:N),K(1:s,1:N))
    tmp=cmplx(0q0,0q0,kind=16); K=cmplx(0q0,0q0,kind=16)
    if(present(SS))SS(1:N)=cmplx(0q0,0q0,kind=16)
   
    h=xbound-x
   
    if(abs(h).ge.hmax)then
       if(h.le.0q0)then
          h=-hmax
       else
          h=hmax
       endif
    endif
    FLAG=1
    if(abs(x-xbound).le.hmin*0.1q0)then
       info=1
       FLAG=0
    endif
    do while(FLAG.eq.1)
       tx=x
       do j=1,s
          tx=x+c(j)*h
          tmp(1:N)=y(1:N)
          do i=1,j-1
             tmp(1:N)=tmp(1:N)+K(i,1:N)*a(j,i)
          enddo
          do i=1,N
             K(j,i)=h*func(N,tx,tmp,i)
          enddo
       enddo
       !step 4
       R=0q0
       do i=1,N
          R=R+abs(Rc(1)*K(1,i)+Rc(3)*K(3,i)+Rc(4)*K(4,i)+Rc(5)*K(5,i)+Rc(6)*K(6,i))**2
       enddo
       R=sqrt(R)/h
       Sy=0q0
       do i=1,N
          Sy=Sy+abs(y(i))**2
       enddo
       Sy=sqrt(Sy)
       if(Sy.ge.1q0)then
          err=tol*Sy
       else
          err=tol
       endif
       if(R.le.err.or.key.eq.1)then
          if(present(SS))then
             x=x+h
             do i=1,s
                SS(1:N)=SS(1:N)+b1(i)*K(i,1:N)
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          else
             x=x+h
             do i=1,s
                y(1:N)=y(1:N)+b1(i)*K(i,1:N)
             enddo
          endif
          key=0
       endif
       !step 6
       !  Avoid zero deviding.
       if(R.ge.1q-40)then
          delta=(err/(2q0*R))**0.25q0
       else
          delta=4q0
       endif
       !step 7
       if(delta.le.0.1q0)then
          !function changes dramatically.
          h=0.1q0*h
       elseif(delta.ge.4q0)then
          !function changes loosely.
          h=4q0*h
       else
          !function changes moderately.
          h=delta*h
       endif
       !step 8
       if(abs(h).ge.hmax)then
          h=sign(1q0,h)*hmax
       elseif(abs(h).lt.hmin)then
          h=sign(1q0,h)*hmin
          key=1
          disc=1
       endif
       
       !step 9
       if(abs(xbound-x).le.abs(h))then
          h=xbound-x
          if(abs(h).le.hmin)then
             info=1
             FLAG=0
          endif
       end if
    enddo
   
    if(disc.eq.1)then
       info=-1
    endif
       
    deallocate(tmp,K)
    return
  end subroutine zrkf451_e
end module qRKmod