submodule (lightkrylov_basekrylov) ssy_method
    implicit none (type, external)
contains
    module procedure ssy_rsp
        character(len=*), parameter :: this_procedure = "ssy_rsp"
        character(len=100) :: errmsg
        integer :: k_start, k_end, i, k
        real(sp) :: tolerance
        real(sp) :: beta, gamma

        if (time_lightkrylov()) call timer%start(this_procedure)
        associate(kdim => size(U)-1)

            ! Deals with optional arguments.
            k_start = optval(kstart, 1) ; k_end = optval(kend, kdim)
            tolerance = optval(tol, atol_sp)

            ! Saunders-Simon-Yip tridiagonalization.
            if (k_start == 1) then
                beta = U(1)%norm()
                call U(1)%scal(one_rsp / beta)

                gamma = V(1)%norm()
                call V(1)%scal(one_rsp / gamma)
            endif

            do k = k_start, k_end
                ! Compute q = A @ v[k] - gamma[k]*u[k-1]
                call A%matvec(V(k), U(k+1))
                if (k > 1) call U(k+1)%axpby(-gamma, U(k-1), one_rsp)

                ! Compute alpha[k] = dot(u[k], q)
                T(k, k) = U(k)%dot(U(k+1))

                ! Compute beta[k+1] * u[k+1] = q - alpha[k]*u[k]
                call double_gram_schmidt_step(U(k+1), U(:k), info, if_chk_orthonormal=.false.)
                beta = U(k+1)%norm() ; T(k+1, k) = beta
                if (abs(beta) > tolerance) then
                    call U(k+1)%scal(one_rsp / beta)
                else
                    info = k
                    exit
                endif

                ! Compute gamma[k+1]*v[k+1] = hermitian(A) @ u[k] - beta[k]*v[k-1] - alpha[k]*v[k]
                call A%rmatvec(U(k), V(k+1))
                call double_gram_schmidt_step(V(k+1), V(:k), info, if_chk_orthonormal=.false.)
                ! if (k > 1) call V(k+1)%axpby(-T(k, k-1), V(k-1), one_rsp)
                ! call V(k+1)%axpby(-T(k, k), V(k), one_rsp)
                gamma = V(k+1)%norm() ; T(k, k+1) = gamma
                if (abs(gamma) > tolerance) then
                    call V(k+1)%scal(one_rsp / gamma)
                else
                    info = k
                    exit
                endif
            enddo

        end associate
        if (time_lightkrylov()) call timer%stop(this_procedure)
    end procedure ssy_rsp
    module procedure ssy_rdp
        character(len=*), parameter :: this_procedure = "ssy_rdp"
        character(len=100) :: errmsg
        integer :: k_start, k_end, i, k
        real(dp) :: tolerance
        real(dp) :: beta, gamma

        if (time_lightkrylov()) call timer%start(this_procedure)
        associate(kdim => size(U)-1)

            ! Deals with optional arguments.
            k_start = optval(kstart, 1) ; k_end = optval(kend, kdim)
            tolerance = optval(tol, atol_dp)

            ! Saunders-Simon-Yip tridiagonalization.
            if (k_start == 1) then
                beta = U(1)%norm()
                call U(1)%scal(one_rdp / beta)

                gamma = V(1)%norm()
                call V(1)%scal(one_rdp / gamma)
            endif

            do k = k_start, k_end
                ! Compute q = A @ v[k] - gamma[k]*u[k-1]
                call A%matvec(V(k), U(k+1))
                if (k > 1) call U(k+1)%axpby(-gamma, U(k-1), one_rdp)

                ! Compute alpha[k] = dot(u[k], q)
                T(k, k) = U(k)%dot(U(k+1))

                ! Compute beta[k+1] * u[k+1] = q - alpha[k]*u[k]
                call double_gram_schmidt_step(U(k+1), U(:k), info, if_chk_orthonormal=.false.)
                beta = U(k+1)%norm() ; T(k+1, k) = beta
                if (abs(beta) > tolerance) then
                    call U(k+1)%scal(one_rdp / beta)
                else
                    info = k
                    exit
                endif

                ! Compute gamma[k+1]*v[k+1] = hermitian(A) @ u[k] - beta[k]*v[k-1] - alpha[k]*v[k]
                call A%rmatvec(U(k), V(k+1))
                call double_gram_schmidt_step(V(k+1), V(:k), info, if_chk_orthonormal=.false.)
                ! if (k > 1) call V(k+1)%axpby(-T(k, k-1), V(k-1), one_rdp)
                ! call V(k+1)%axpby(-T(k, k), V(k), one_rdp)
                gamma = V(k+1)%norm() ; T(k, k+1) = gamma
                if (abs(gamma) > tolerance) then
                    call V(k+1)%scal(one_rdp / gamma)
                else
                    info = k
                    exit
                endif
            enddo

        end associate
        if (time_lightkrylov()) call timer%stop(this_procedure)
    end procedure ssy_rdp
    module procedure ssy_csp
        character(len=*), parameter :: this_procedure = "ssy_csp"
        character(len=100) :: errmsg
        integer :: k_start, k_end, i, k
        real(sp) :: tolerance
        complex(sp) :: beta, gamma

        if (time_lightkrylov()) call timer%start(this_procedure)
        associate(kdim => size(U)-1)

            ! Deals with optional arguments.
            k_start = optval(kstart, 1) ; k_end = optval(kend, kdim)
            tolerance = optval(tol, atol_sp)

            ! Saunders-Simon-Yip tridiagonalization.
            if (k_start == 1) then
                beta = U(1)%norm()
                call U(1)%scal(one_csp / beta)

                gamma = V(1)%norm()
                call V(1)%scal(one_csp / gamma)
            endif

            do k = k_start, k_end
                ! Compute q = A @ v[k] - gamma[k]*u[k-1]
                call A%matvec(V(k), U(k+1))
                if (k > 1) call U(k+1)%axpby(-gamma, U(k-1), one_csp)

                ! Compute alpha[k] = dot(u[k], q)
                T(k, k) = U(k)%dot(U(k+1))

                ! Compute beta[k+1] * u[k+1] = q - alpha[k]*u[k]
                call double_gram_schmidt_step(U(k+1), U(:k), info, if_chk_orthonormal=.false.)
                beta = U(k+1)%norm() ; T(k+1, k) = beta
                if (abs(beta) > tolerance) then
                    call U(k+1)%scal(one_csp / beta)
                else
                    info = k
                    exit
                endif

                ! Compute gamma[k+1]*v[k+1] = hermitian(A) @ u[k] - beta[k]*v[k-1] - alpha[k]*v[k]
                call A%rmatvec(U(k), V(k+1))
                call double_gram_schmidt_step(V(k+1), V(:k), info, if_chk_orthonormal=.false.)
                ! if (k > 1) call V(k+1)%axpby(-T(k, k-1), V(k-1), one_csp)
                ! call V(k+1)%axpby(-T(k, k), V(k), one_csp)
                gamma = V(k+1)%norm() ; T(k, k+1) = gamma
                if (abs(gamma) > tolerance) then
                    call V(k+1)%scal(one_csp / gamma)
                else
                    info = k
                    exit
                endif
            enddo

        end associate
        if (time_lightkrylov()) call timer%stop(this_procedure)
    end procedure ssy_csp
    module procedure ssy_cdp
        character(len=*), parameter :: this_procedure = "ssy_cdp"
        character(len=100) :: errmsg
        integer :: k_start, k_end, i, k
        real(dp) :: tolerance
        complex(dp) :: beta, gamma

        if (time_lightkrylov()) call timer%start(this_procedure)
        associate(kdim => size(U)-1)

            ! Deals with optional arguments.
            k_start = optval(kstart, 1) ; k_end = optval(kend, kdim)
            tolerance = optval(tol, atol_dp)

            ! Saunders-Simon-Yip tridiagonalization.
            if (k_start == 1) then
                beta = U(1)%norm()
                call U(1)%scal(one_cdp / beta)

                gamma = V(1)%norm()
                call V(1)%scal(one_cdp / gamma)
            endif

            do k = k_start, k_end
                ! Compute q = A @ v[k] - gamma[k]*u[k-1]
                call A%matvec(V(k), U(k+1))
                if (k > 1) call U(k+1)%axpby(-gamma, U(k-1), one_cdp)

                ! Compute alpha[k] = dot(u[k], q)
                T(k, k) = U(k)%dot(U(k+1))

                ! Compute beta[k+1] * u[k+1] = q - alpha[k]*u[k]
                call double_gram_schmidt_step(U(k+1), U(:k), info, if_chk_orthonormal=.false.)
                beta = U(k+1)%norm() ; T(k+1, k) = beta
                if (abs(beta) > tolerance) then
                    call U(k+1)%scal(one_cdp / beta)
                else
                    info = k
                    exit
                endif

                ! Compute gamma[k+1]*v[k+1] = hermitian(A) @ u[k] - beta[k]*v[k-1] - alpha[k]*v[k]
                call A%rmatvec(U(k), V(k+1))
                call double_gram_schmidt_step(V(k+1), V(:k), info, if_chk_orthonormal=.false.)
                ! if (k > 1) call V(k+1)%axpby(-T(k, k-1), V(k-1), one_cdp)
                ! call V(k+1)%axpby(-T(k, k), V(k), one_cdp)
                gamma = V(k+1)%norm() ; T(k, k+1) = gamma
                if (abs(gamma) > tolerance) then
                    call V(k+1)%scal(one_cdp / gamma)
                else
                    info = k
                    exit
                endif
            enddo

        end associate
        if (time_lightkrylov()) call timer%stop(this_procedure)
    end procedure ssy_cdp
end submodule ssy_method
