module Triangular_ODE

  implicit NONE
  public

  interface Compute_Z_Matrix
    module procedure Compute_Z_Matrix_D, Compute_Z_Matrix_S
  end interface

  interface Solve_A_C_B
    module procedure Solve_A_C_B_D, Solve_A_C_B_S
  end interface

  interface Compute_Solution
    module procedure Compute_Solution_D, Compute_Solution_S
  end interface

contains

  subroutine Compute_Z_Matrix_D ( A, Z, Status )
    integer, parameter :: RK = kind(0.0d0)
!   real(rk), intent(in) :: A(:,:)    ! Lower triangular activity matrix.
!   real(rk), intent(inout) :: Z(:,:) ! Matrix of solution coefficients.
                                      ! Intent(inout) so as not to make the
                                      ! upper triangle undefined, in case the
                                      ! matrix is printed later.
!   integer, intent(out), optional :: Status ! Index of the last nonzero
                                      ! diagonal element of A if there are no
                                      ! subsequent nonzero elements, else the
                                      ! negative of the first nonzero element
                                      ! after the first zero element.
    include "Compute_Z_Matrix.f9h"
  end subroutine Compute_Z_Matrix_D

  subroutine Compute_Z_Matrix_S ( A, Z, Status )
    integer, parameter :: RK = kind(0.0e0)
!   real(rk), intent(in) :: A(:,:)    ! Lower triangular activity matrix.
!   real(rk), intent(inout) :: Z(:,:) ! Matrix of solution coefficients.
                                      ! Intent(inout) so as not to make the
                                      ! upper triangle undefined, in case the
                                      ! matrix is printed later.
!   integer, intent(out), optional :: Status ! Index of the last nonzero
                                      ! diagonal element of A if there are no
                                      ! subsequent nonzero elements, else the
                                      ! negative of the first nonzero element
                                      ! after the first zero element.
    include "Compute_Z_Matrix.f9h"
  end subroutine Compute_Z_Matrix_S

  subroutine Solve_A_C_B_D ( A, C, B, Status )
    integer, parameter :: RK = kind(0.0d0)
!   real(rk), intent(in) :: A(:,:) ! Lower triangular coefficient matrix
!   real(rk), intent(in) :: C(:)   ! Right hand side of A B = C
!   real(rk), intent(out) :: B(:)  ! Solution of A_m C_m = B_m, where M is the
                                   ! index of the last nonzero diagonal in A
!   integer, intent(out) :: Status ! Negative of M, the last nonzero diagonal
                                   ! in A, if there are no nonzeroes after
                                   ! M, else the index of the first nonzero
                                   ! after M
    include "Solve_A_C_B.f9h"
  end subroutine Solve_A_C_B_D

  subroutine Solve_A_C_B_S ( A, C, B, Status )
    integer, parameter :: RK = kind(0.0e0)
!   real(rk), intent(in) :: A(:,:) ! Lower triangular coefficient matrix
!   real(rk), intent(in) :: C(:)   ! Right hand side of A B = C
!   real(rk), intent(out) :: B(:)  ! Solution of A_m B_m = C_m, where M is the
                                   ! index of the last nonzero diagonal in A
!   integer, intent(out) :: Status ! Negative of M, the last nonzero diagonal
                                   ! in A, if there are no nonzeroes after
                                   ! M, else the index of the first nonzero
                                   ! after M
    include "Solve_A_C_B.f9h"
  end subroutine Solve_A_C_B_S

  subroutine Compute_Solution_D ( T, A, Z, N0, N_T, B )
    integer, parameter :: RK = kind(0.0d0)
!   real(rk), intent(in) :: T          ! Independent variable
!   real(rk), intent(in) :: A(:,:)     ! Coefficient matrix for ODE
!   real(rk), intent(in) :: Z(:,:)     ! Coefficient matrix solutions
!   real(rk), intent(in) :: N0(:)      ! Initial condition
!   real(rk), intent(out) :: N_T(:)    ! Solution N(T)
!   real(rk), intent(in), optional :: B(:)  ! For inhomogeneous equations.
                                       ! Let M be the index in A of the last
                                       ! nonzero diagonal element. Then
                                       ! B(1:m) is B in
                                       !   d N(t)/d t = A ( N(t) + B )
                                       ! and B(m+1:) is C in
                                       !   d N(t)/d t = A N(t) + C.
                                       ! Use Solve_A_C_B to compute B.
    include "Compute_Solution.f9h"
  end subroutine Compute_Solution_D

  subroutine Compute_Solution_S ( T, A, Z, N0, N_T, B )
    integer, parameter :: RK = kind(0.0e0)
!   real(rk), intent(in) :: T          ! Independent variable
!   real(rk), intent(in) :: A(:,:)     ! Coefficient matrix for ODE
!   real(rk), intent(in) :: Z(:,:)     ! Coefficient matrix solutions
!   real(rk), intent(in) :: N0(:)      ! Initial condition
!   real(rk), intent(out) :: N_T(:)    ! Solution N(T)
!   real(rk), intent(in), optional :: B(:)  ! For inhomogeneous equations.
                                       ! Let M be the index in A of the last
                                       ! nonzero diagonal element. Then
                                       ! B(1:m) is B in
                                       !   d N(t)/d t = A ( N(t) + B )
                                       ! and B(m+1:) is C in
                                       !   d N(t)/d t = A N(t) + C.
                                       ! Use Solve_A_C_B to compute B.
    include "Compute_Solution.f9h"
  end subroutine Compute_Solution_S

end module Triangular_ODE
