!===============================================================================
!
! cg_sc2_projection.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 "defs.h"
# include "cg_defs.h"
# include "simd.h"

!-------------------------------------------------------------------------------
#ifdef DAGGER
# define PLUS -
# define MINUS +
# define D_T_ONE 3
# define D_T_TWO 4
# define D_T_THREE 1 
# define D_T_FOUR 2
# define D_T_FWD 1
# define D_T_BWD 2
#else
# define PLUS +
# define MINUS -
# define D_T_ONE 1
# define D_T_TWO 2
# define D_T_THREE 3 
# define D_T_FOUR 4
# define D_T_FWD 2
# define D_T_BWD 1
#endif

!-------------------------------------------------------------------------------
#ifdef DAGGER
# define cSUM(a,b,c)         cDiff(a,b,c)
# define cDIFF(a,b,c)        cSum(a,b,c)
# define cSUMi_times(a,b,c)  cDiffItimes(a,b,c)
# define cDIFFi_times(a,b,c) cSumItimes(a,b,c)
#else
# define cSUM(a,b,c)         cSum(a,b,c)
# define cDIFF(a,b,c)        cDiff(a,b,c)
# define cSUMi_times(a,b,c)  cSumItimes(a,b,c)
# define cDIFFi_times(a,b,c) cDiffItimes(a,b,c)
#endif

#ifdef SIMD
# define PRIVATE_VARS_01 in11r, in21r, in31r, in41r, in12r, in22r, in32r, in42r, in13r, in23r, in33r, in43r
# define PRIVATE_VARS_02 in11i, in21i, in31i, in41i, in12i, in22i, in32i, in42i, in13i, in23i, in33i, in43i
# define PRIVATE_VARS_03 ab1_1r, ab1_2r, ab1_3r, ab2_1r, ab2_2r, ab2_3r
# define PRIVATE_VARS_04 ab1_1i, ab1_2i, ab1_3i, ab2_1i, ab2_2i, ab2_3i
# define PRIVATE_VARS_05 af1_1r, af1_2r, af1_3r, af2_1r, af2_2r, af2_3r
# define PRIVATE_VARS_06 af1_1i, af1_2i, af1_3i, af2_1i, af2_2i, af2_3i
#endif

!-------------------------------------------------------------------------------
#ifdef DAGGER
# define NAME cg_sc2_projection_dag
#else
# define NAME cg_sc2_projection
#endif

# define TRACE_NAME T(NAME)
!-------------------------------------------------------------------------------
subroutine T(NAME)(out, in, volh_tot, varsBLOCK)

  use module_simd
  implicit none
  integer, intent(in)       :: volh_tot, varsBLOCK
  sc2_FIELD, intent(out)    :: out
  spincol_FIELD, intent(in) :: in

  integer :: i

#ifdef SIMD
  SIMD_VECTOR :: PRIVATE_VARS_01
  SIMD_VECTOR :: PRIVATE_VARS_02
  SIMD_VECTOR :: PRIVATE_VARS_03
  SIMD_VECTOR :: PRIVATE_VARS_04
  SIMD_VECTOR :: PRIVATE_VARS_05
  SIMD_VECTOR :: PRIVATE_VARS_06
#else
# include "i_times.h"
#endif


  TRACE_ENTRY
  TIMING_START(timing_bin_sc2_projection)

  ALIGNX(out)
  ALIGNX(in)

#ifndef BLOCK
  !$omp parallel do &
#ifdef SIMD
  !$omp             private(PRIVATE_VARS_01) &
  !$omp             private(PRIVATE_VARS_02) &
  !$omp             private(PRIVATE_VARS_03) &
  !$omp             private(PRIVATE_VARS_04) &
  !$omp             private(PRIVATE_VARS_05) &
  !$omp             private(PRIVATE_VARS_06) &
#endif
  !$omp             private(i)
  do i = 1, varsBLOCK
#else
  do i = varsBLOCK
#endif

#ifndef SIMD
DO c = 1, 3
     out(1, c, i, 1, FWD) = Spincol(in, 1, 1, c, i) MINUS i_times(Spincol(in, 2, 2, c, i))
     out(2, c, i, 1, FWD) = Spincol(in, 2, 1, c, i) MINUS i_times(Spincol(in, 1, 2, c, i))
     out(1, c, i, 1, BWD) = Spincol(in, 1, 1, c, i) PLUS  i_times(Spincol(in, 2, 2, c, i))
     out(2, c, i, 1, BWD) = Spincol(in, 2, 1, c, i) PLUS  i_times(Spincol(in, 1, 2, c, i))

     out(1, c, i, 2, FWD) = Spincol(in, 1, 1, c, i) MINUS Spincol(in, 2, 2, c, i)
     out(2, c, i, 2, FWD) = Spincol(in, 2, 1, c, i) PLUS  Spincol(in, 1, 2, c, i)
     out(1, c, i, 2, BWD) = Spincol(in, 1, 1, c, i) PLUS  Spincol(in, 2, 2, c, i)
     out(2, c, i, 2, BWD) = Spincol(in, 2, 1, c, i) MINUS Spincol(in, 1, 2, c, i)

     out(1, c, i, 3, FWD) = Spincol(in, 1, 1, c, i) MINUS i_times(Spincol(in, 1, 2, c, i))
     out(2, c, i, 3, FWD) = Spincol(in, 2, 1, c, i) PLUS  i_times(Spincol(in, 2, 2, c, i))
     out(1, c, i, 3, BWD) = Spincol(in, 1, 1, c, i) PLUS  i_times(Spincol(in, 1, 2, c, i))
     out(2, c, i, 3, BWD) = Spincol(in, 2, 1, c, i) MINUS i_times(Spincol(in, 2, 2, c, i))

     out(1, c, i, 4, FWD) = Spincol(in, 1, D_T_FWD, c, i)
     out(2, c, i, 4, FWD) = Spincol(in, 2, D_T_FWD, c, i)
     out(1, c, i, 4, BWD) = Spincol(in, 1, D_T_BWD, c, i)
     out(2, c, i, 4, BWD) = Spincol(in, 2, D_T_BWD, c, i)
ENDDO

#else
DO c = 1, 3
DO s2 = 1, 2
DO s1 = 1, 2
LET s = s1+(s2-1)*2
     cLoad(in %% s %% c, Spincol(in, s1, s2, c, i))
ENDLET
ENDDO
ENDDO

     cDIFFi_times(STRCAT(af1_,c), STRCAT(in1,c), STRCAT(in4,c))
     cDIFFi_times(STRCAT(af2_,c), STRCAT(in2,c), STRCAT(in3,c))
      cSUMi_times(STRCAT(ab1_,c), STRCAT(in1,c), STRCAT(in4,c))
      cSUMi_times(STRCAT(ab2_,c), STRCAT(in2,c), STRCAT(in3,c))

     cStore(out(1, c, i, 1, FWD), STRCAT(af1_,c))
     cStore(out(2, c, i, 1, FWD), STRCAT(af2_,c))
     cStore(out(1, c, i, 1, BWD), STRCAT(ab1_,c))
     cStore(out(2, c, i, 1, BWD), STRCAT(ab2_,c))

            cDIFF(STRCAT(af1_,c), STRCAT(in1,c), STRCAT(in4,c))
             cSUM(STRCAT(af2_,c), STRCAT(in2,c), STRCAT(in3,c))
             cSUM(STRCAT(ab1_,c), STRCAT(in1,c), STRCAT(in4,c))
            cDIFF(STRCAT(ab2_,c), STRCAT(in2,c), STRCAT(in3,c))

     cStore(out(1, c, i, 2, FWD), STRCAT(af1_,c))
     cStore(out(2, c, i, 2, FWD), STRCAT(af2_,c))
     cStore(out(1, c, i, 2, BWD), STRCAT(ab1_,c))
     cStore(out(2, c, i, 2, BWD), STRCAT(ab2_,c))

     cDIFFi_times(STRCAT(af1_,c), STRCAT(in1,c), STRCAT(in3,c))
      cSUMi_times(STRCAT(af2_,c), STRCAT(in2,c), STRCAT(in4,c))
      cSUMi_times(STRCAT(ab1_,c), STRCAT(in1,c), STRCAT(in3,c))
     cDIFFi_times(STRCAT(ab2_,c), STRCAT(in2,c), STRCAT(in4,c))

     cStore(out(1, c, i, 3, FWD), STRCAT(af1_,c))
     cStore(out(2, c, i, 3, FWD), STRCAT(af2_,c))
     cStore(out(1, c, i, 3, BWD), STRCAT(ab1_,c))
     cStore(out(2, c, i, 3, BWD), STRCAT(ab2_,c))

     cStore(out(1, c, i, 4, FWD), STRCAT3(in,D_T_THREE,c))
     cStore(out(2, c, i, 4, FWD), STRCAT3(in,D_T_FOUR,c))
     cStore(out(1, c, i, 4, BWD), STRCAT3(in,D_T_ONE,c))
     cStore(out(2, c, i, 4, BWD), STRCAT3(in,D_T_TWO,c))

ENDDO
#endif

  enddo

  TIMING_STOP(timing_bin_sc2_projection)
  TRACE_RETURN

end

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