! 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_cross
   use, intrinsic :: ieee_exceptions
   use swiftest
   !! author: David A. Minton
   !!
   !! Contains implementations for the .cross. operator for all defined integer and real types
   !! Computes the cross product of two (3) vectors or (3,:) arrays 
   !! Single vector implementations: C(1:3)   = A(1:3)   .cross. B(1:3) 
   !! Vector list implementations:   C(1:3, :) = A(1:3, :) .cross. B(1:3, :)
contains

   pure module function operator_cross_sp(A, B) result(C)
      implicit none
      real(SP), dimension(:), intent(in) :: A, B
      real(SP), dimension(3) :: C

      call ieee_set_halting_mode(ieee_underflow, .false.)
      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_sp

   pure module function operator_cross_dp(A, B) result(C)
      implicit none
      real(DP), dimension(:), intent(in) :: A, B
      real(DP), dimension(3) :: C

      call ieee_set_halting_mode(ieee_underflow, .false.)
      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_dp

#ifdef QUADPREC
   pure module function operator_cross_qp(A, B) result(C)
      implicit none
      real(QP), dimension(:), intent(in) :: A, B
      real(QP), dimension(3) :: C

      call ieee_set_halting_mode(ieee_underflow, .false.)
      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_qp
#endif

   pure module function operator_cross_i1b(A, B) result(C)
      implicit none
      integer(I1B), dimension(:), intent(in) :: A, B
      integer(I1B), dimension(3) :: C

      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_i1b

   pure module function operator_cross_i2b(A, B) result(C)
      implicit none
      integer(I2B), dimension(:), intent(in) :: A, B
      integer(I2B), dimension(3) :: C

      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_i2b

   pure module function operator_cross_i4b(A, B) result(C)
      implicit none
      integer(I4B), dimension(:), intent(in) :: A, B
      integer(I4B), dimension(3) :: C
      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_i4b

   pure module function operator_cross_i8b(A, B) result(C)     
      implicit none
      integer(I8B), dimension(:), intent(in) :: A, B
      integer(I8B), dimension(3) :: C
      C(1) = A(2) * B(3) - A(3) * B(2)
      C(2) = A(3) * B(1) - A(1) * B(3)
      C(3) = A(1) * B(2) - A(2) * B(1)
      return
   end function operator_cross_i8b

   pure module function operator_cross_el_sp(A, B) result(C)
      implicit none
      real(SP), dimension(:,:), intent(in)  :: A, B
      real(SP), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_sp(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_sp

   pure module function operator_cross_el_dp(A, B) result(C)
      implicit none
      real(DP), dimension(:,:), intent(in)  :: A, B
      real(DP), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_dp(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_dp

#ifdef QUADPREC
   pure module function operator_cross_el_qp(A, B) result(C)
      implicit none
      real(QP), dimension(:,:), intent(in)  :: A, B
      real(QP), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_qp(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_qp
#endif 

   pure module function operator_cross_el_i1b(A, B) result(C)
      implicit none
      integer(I1B), dimension(:,:), intent(in)  :: A, B
      integer(I1B), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_i1b(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_i1b

   pure module function operator_cross_el_i2b(A, B) result(C)
      implicit none
      integer(I2B), dimension(:,:), intent(in)  :: A, B
      integer(I2B), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_i2b(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_i2b

   pure module function operator_cross_el_i4b(A, B) result(C)
      implicit none
      integer(I4B), dimension(:,:), intent(in)  :: A, B
      integer(I4B), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_i4b(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_i4b

   pure module function operator_cross_el_i8b(A, B) result(C)
      implicit none
      integer(I8B), dimension(:,:), intent(in)  :: A, B
      integer(I8B), dimension(:,:), allocatable :: C
      integer(I4B) :: i, n
      n = size(A, 2)
      if (allocated(C)) deallocate(C)
      allocate(C, mold = A)
      do i = 1,n 
         C(:,i) = operator_cross_i8b(A(:,i), B(:,i))
      end do
      return
   end function operator_cross_el_i8b

end submodule s_operator_cross