! Copyright 2024 - The Minton Group at Purdue University
! This file is part of Swiftest.
! Swiftest is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License 
! as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
! Swiftest is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
! of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
! You should have received a copy of the GNU General Public License along with Swiftest. 
! If not, see: https://www.gnu.org/licenses. 

submodule(operators) s_operator_unit
   use, intrinsic :: ieee_exceptions
   !! author: David A. Minton
   !!
   !! Contains implementations for the .unit. operator for all defined real types
   !! Returns a unit vector or array of unit vectors from an input vector or array of vectors
   !! Single vector implementations:  B   = .unit. A(1:NDIM)
   !! Vector list implementations:   B(:) = .unit. A(1:NDIM, :)
contains

   pure module function operator_unit_sp(A) result(B)
      implicit none
      ! Arguments
      real(SP), dimension(:), intent(in) :: A
      real(SP), dimension(NDIM)          :: B
      ! Internals
      real(SP)  :: Amag

      call ieee_set_halting_mode(ieee_underflow, .false.)
      Amag = norm2(A(:))
      if (Amag > tiny(1._SP)) then
         B(:) = A(:) / Amag
      else
         B(:) = 0.0_SP
      end if

      return
   end function operator_unit_sp


   pure module function operator_unit_dp(A) result(B)
      implicit none
      ! Arguments
      real(DP), dimension(:), intent(in) :: A
      real(DP), dimension(NDIM)             :: B
      ! Internals
      real(DP)  :: Amag

      call ieee_set_halting_mode(ieee_underflow, .false.)
      Amag = norm2(A(:))
      if (Amag > tiny(1._DP)) then
         B(:) = A(:) / Amag
      else
         B(:) = 0.0_DP
      end if

      return
   end function operator_unit_dp

#ifdef QUADPREC
   pure module function operator_unit_qp(A) result(B)
      implicit none
      ! Arguments
      real(QP), dimension(:), intent(in) :: A
      real(QP), dimension(NDIM)             :: B
      ! Internals
      real(QP)  :: Amag

      call ieee_set_halting_mode(ieee_underflow, .false.)
      Amag = norm2(A(:))
      if (Amag > tiny(1._QP)) then
         B(:) = A(:) / Amag
      else
         B(:) = 0.0_QP
      end if

      return
   end function operator_unit_qp
#endif

   pure module function operator_unit_el_sp(A) result(B)
      implicit none
      ! Arguments
      real(SP), dimension(:,:), intent(in)   :: A
      real(SP), dimension(:,:), allocatable  :: B
      ! Internals
      integer(I4B)  :: i,n

      n = size(A, 2)
      if (allocated(B)) deallocate(B)
      allocate(B(NDIM,n))

      do i=1,n
         B(:,i) = operator_unit_sp(A(:,i))
      end do

      return
   end function operator_unit_el_sp


   pure module function operator_unit_el_dp(A) result(B)
      implicit none
      ! Arguments
      real(DP), dimension(:,:), intent(in)   :: A
      real(DP), dimension(:,:), allocatable  :: B
      ! Internals
      integer(I4B)  :: i,n

      n = size(A, 2)
      if (allocated(B)) deallocate(B)
      allocate(B(NDIM,n))

      do i=1,n
         B(:,i) = operator_unit_dp(A(:,i))
      end do

      return
   end function operator_unit_el_dp

#ifdef QUADPREC
   pure module function operator_unit_el_qp(A) result(B)
      implicit none
      ! Arguments
      real(QP), dimension(:,:), intent(in)   :: A
      real(QP), dimension(:,:), allocatable  :: B
      ! Internals
      integer(I4B)  :: i,n

      n = size(A, 2)
      if (allocated(B)) deallocate(B)
      allocate(B(NDIM,n))

      do i=1,n
         B(:,i) = operator_unit_qp(A(:,i))
      end do

      return 
   end function operator_unit_el_qp
#endif

end submodule s_operator_unit

