!===============================================================================
!
! index2.F90 - more functions for index calculations
!              these functions use modules
!
!-------------------------------------------------------------------------------
!
! Copyright (C) 2006-2007 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/>.
!
!-------------------------------------------------------------------------------
# include "defs.h"

!-------------------------------------------------------------------------------
subroutine local2global(home, local, global)  ! local -> global (lattice coords)

  use module_lattice
  implicit none

  integer, intent(in)                 :: home   ! home process of "local"
  integer, intent(in), dimension(DIM) :: local  ! local coordinates 
  integer, intent(out),dimension(DIM) :: global ! global coordinates

  integer, external                   :: i_global, i_periodic
  integer                             :: coord_home(DIM), i
  
  call pe2coord(home, coord_home)

  do i = 1, DIM
     global(i) = i_global(local(i), N(i), coord_home(i))
     global(i) = i_periodic(global(i), L(i))
  enddo

end

!-------------------------------------------------------------------------------
subroutine global2local(global, home, local)  ! global -> local (lattice coords)

  use module_lattice
  implicit none

  integer, intent(in), dimension(DIM) :: global ! global coordinates
  integer, intent(out)                :: home   ! home process of "local"
  integer, intent(out),dimension(DIM) :: local  ! local coordinates

  integer, external                   :: coord2pe
  integer, dimension(DIM)             :: coord
  integer                             :: i
  
  do i = 1, DIM
     ASSERT(global(i) >= 0)
     ASSERT(global(i) < L(i))
     call i_home(global(i), N(i), coord(i), local(i))
  enddo

  home = coord2pe(coord)

end

!-------------------------------------------------------------------------------
subroutine global2remote_local(global, remote, local)  ! local coords on remote process

  use module_lattice
  implicit none

  integer, intent(in), dimension(DIM) :: global ! global coordinates
  integer, intent(in)                 :: remote ! remote process
  integer, intent(out),dimension(DIM) :: local  ! local coordinates 

  integer, external                   :: i_local, i_periodic
  integer                             :: coord_remote(DIM), i
  
  call pe2coord(remote, coord_remote)

  do i = 1, DIM
     ASSERT(global(i) >= 0)
     ASSERT(global(i) < L(i))

     if (coord_remote(i) == 0 .and. global(i) == L(i) - 1) then
        local(i) = -1
     elseif (coord_remote(i) == NPE(i) - 1 .and. global(i) == 0) then
        local(i) = N(i)
     else
        local(i) = i_local(global(i), N(i), coord_remote(i))
     endif

     ASSERT(local(i) >= -1)
     ASSERT(local(i) <= N(i))
     if (NPE(i) == 1) local(i) = i_periodic(local(i), N(i))
  enddo

end

!-------------------------------------------------------------------------------
integer function e_o(local)  !// returns EVEN or ODD (0 or 1)

  use module_function_decl
  implicit none

  integer, intent(in), dimension(DIM) :: local  ! local coordinates
  integer, dimension(DIM)             :: global ! global coordinates
  integer, external                   :: i_e_o

  call local2global(my_pe(), local, global)

  e_o = i_e_o(DIM, global)
end

!-------------------------------------------------------------------------------
subroutine pe2coord(pe, coord)  ! returns coordinates of process pe

  use module_lattice
  implicit none
  integer, intent(in)                  :: pe
  integer, intent(out), dimension(DIM) :: coord

  call unlex_perm(pe, DIM, coord, npe, process_mapping)
end

!-------------------------------------------------------------------------------
integer function coord2pe(coord)  ! returns process id

  use module_lattice
  implicit none
  integer, intent(in), dimension(DIM) :: coord
  integer, external                   :: ilex_perm

  coord2pe = ilex_perm(DIM, coord, npe, process_mapping)
end

!-------------------------------------------------------------------------------
subroutine my_coord(coord)  ! returns process coordinates

  use module_function_decl
  use module_lattice
  implicit none
  integer, intent(out), dimension(DIM) :: coord
  integer :: me
  
  me = my_pe()

  call pe2coord(me, coord)
end

!-------------------------------------------------------------------------------
subroutine coord_test()

  use module_function_decl
  use module_lattice  
  implicit none

  integer :: coord(DIM)
  integer :: pe


  call my_coord(coord)
  ASSERT(my_pe() == coord2pe(coord))

  do pe = 0, num_pes() - 1
     call pe2coord(pe, coord)
     ASSERT(pe == coord2pe(coord))
  enddo


  call open_diag()

  do pe = 0, num_pes() - 1
     call pe2coord(pe, coord)
     write(UDIAG,*) pe, coord
  enddo

end

!-------------------------------------------------------------------------------
subroutine ieo2local(ieo, eo, local)  ! returns local coords for given (ieo, eo)
                                      ! (inverse of xyzt2i() and e_o())
  use module_function_decl
  use module_lattice                  !//  provides N
  use module_vol
  implicit none
  integer, intent(in)  :: ieo         !//  even/odd index
  integer, intent(in)  :: eo          !//  EVEN or ODD
  integer, intent(out) :: local(DIM)  !//  local lattice coords (x,y,z,t)
  integer              :: i
  integer, intrinsic   :: ieor
  integer, external    :: i_e_o, e_o

  ASSERT(ieo >= 1)
  ASSERT(ieo <= volh)

  i = ieo - 1

  call unlex(2 * i, DIM, local, N)
  local(1) =  local(1) &
           + ieor( ieor(i_e_o(DIM - 1, local(2)), eo), e_o((/0,0,0,0/)) )

end

!-------------------------------------------------------------------------------
subroutine ieo2local_test()

  use module_lattice
  implicit none
  integer :: x, y, z, t
  integer :: i, eo
  integer :: local(DIM)
  integer, external :: xyzt2i, e_o

  do t = 0, NT - 1
  do z = 0, NZ - 1
  do y = 0, NY - 1
  do x = 0, NX - 1

     i = xyzt2i((/x, y, z, t/))
     eo = e_o((/x, y, z, t/))

     call ieo2local(i, eo, local)

     ASSERT(local(1) == x)
     ASSERT(local(2) == y)
     ASSERT(local(3) == z)
     ASSERT(local(4) == t)
  enddo
  enddo
  enddo
  enddo

end

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