!===============================================================================
!
! simd.F90
!
!-------------------------------------------------------------------------------
!
! Copyright (C) 2012-2015 Hinnerk Stueben
!
! This file is part of BQCD -- Berlin Quantum ChromoDynamics program
!
! BQCD 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.
!
! BQCD 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 BQCD.  If not, see <http://www.gnu.org/licenses/>.
!
!-------------------------------------------------------------------------------
# define LANGUAGE_FORTRAN
# include "simd_generic2.h"

FOR rkind in 4 8
SET _rr = _r %% rkind
SET simd_type = type (simd_real %% _rr)
SET SIMD_VECTOR = SIMD_VECTOR %% $(toupper _rr)
SET SIMD_WIDTH = SIMD_WIDTH %% $(toupper _rr)

!-------------------------------------------------------------------------------
simd_type function simd_zero%%_rr()
  use typedef_simd
  implicit none
  simd_zero%%_rr%r(:) = 0.0_%%rkind
end

!-------------------------------------------------------------------------------
simd_type function simd_loads%%_rr(a)
  use typedef_simd
  implicit none
  real(rkind), intent(in) :: a
  simd_loads%%_rr%r(:) = a
end

!-------------------------------------------------------------------------------
simd_type function simd_load%%_rr(a)
  use typedef_simd
  implicit none
  real(rkind), intent(in) :: a(*)
  integer :: i
  do i = 1, SIMD_WIDTH
     simd_load%%_rr%r(i) = a(i)
  enddo
end

!-------------------------------------------------------------------------------
simd_type function simd_loadu%%_rr(a)  ! 'load unaligned': same function as simd_load()
  use typedef_simd
  implicit none
  real(rkind), intent(in) :: a(*)
  integer :: i
  do i = 1, SIMD_WIDTH
     simd_loadu%%_rr%r(i) = a(i)
  enddo
end

!-------------------------------------------------------------------------------
simd_type function simd_load_fwd%%_rr(a)
  use typedef_simd
  implicit none
  real(rkind), intent(in) :: a(*)
  integer :: i
  do i = 1, SIMD_WIDTH
     simd_load_fwd%%_rr%r(i) = a(i + 1)
  enddo
end

!-------------------------------------------------------------------------------
simd_type function simd_load_bwd%%_rr(a)
  use typedef_simd
  implicit none
  real(rkind), intent(in) :: a(*)
  integer :: i
  do i = 1, SIMD_WIDTH
     simd_load_bwd%%_rr%r(i) = a(i - 1)
  enddo
end

!-------------------------------------------------------------------------------
subroutine simd_store%%_rr(a, b)
  use typedef_simd
  implicit none
  real(rkind), intent(out) :: a(*)
  simd_type, intent(in) :: b
  integer :: i
  do i = 1, SIMD_WIDTH
      a(i) = b%r(i)
  enddo
end

!-------------------------------------------------------------------------------
simd_type function simd_shiftleft2%%_rr(a, b, n)
  use typedef_simd
  implicit none
  real(rkind), intent(in) :: a(*), b(*)
  integer, intent(in) :: n
  integer :: i

  do i = 1, SIMD_WIDTH - n
     simd_shiftleft2%%_rr%r(i) = a(i + n)
  enddo
  do i = 1 + SIMD_WIDTH - n, SIMD_WIDTH
     simd_shiftleft2%%_rr%r(i) = b(i + n - SIMD_WIDTH)
  enddo   
end

!-------------------------------------------------------------------------------
simd_type function simd_sum%%_rr(a, b)
  use typedef_simd
  implicit none
  simd_type, intent(in) :: a, b
  simd_sum%%_rr%r(:) = a%r(:) + b%r(:)
end

!-------------------------------------------------------------------------------
simd_type function simd_diff%%_rr(a, b)
  use typedef_simd
  implicit none
  simd_type, intent(in) :: a, b
  simd_diff%%_rr%r(:) = a%r(:) - b%r(:)
end

!-------------------------------------------------------------------------------
simd_type function simd_prod%%_rr(a, b)
  use typedef_simd
  implicit none
  simd_type, intent(in) :: a, b
  simd_prod%%_rr%r(:) = a%r(:) * b%r(:)
end

!-------------------------------------------------------------------------------
simd_type function simd_fmadd%%_rr(a, b, c)
  use typedef_simd
  implicit none
  simd_type, intent(in) :: a, b, c
  simd_fmadd%%_rr%r(:) = a%r(:) * b%r(:) + c%r(:)
end

!-------------------------------------------------------------------------------
simd_type function simd_fmsub%%_rr(a, b, c)
  use typedef_simd
  implicit none
  simd_type, intent(in) :: a, b, c
  simd_fmsub%%_rr%r(:) = a%r(:) * b%r(:) - c%r(:)
end

!-------------------------------------------------------------------------------
real(rkind) function simd_reduction_sum%%_rr(a)
  use typedef_simd
  implicit none
  simd_type, intent(in) :: a
  real(rkind) :: sum
  integer :: i
  sum = 0
  do i = 1, SIMD_WIDTH
     sum = sum + a%r(i)
  enddo
  simd_reduction_sum%%_rr = sum
end

ENDSET
ENDSET
ENDSET
ENDSET
ENDFOR

!===============================================================================
