!===============================================================================
!
! index2_dd.F90 - for DD  see index2.F90
!
!-------------------------------------------------------------------------------
!
! Copyright (C) 2009-2010 Yoshifumi Nakamura
!
! 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_dd(home, local, global, ddtype)  ! local -> global (lattice coords)

  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  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_dd(home, coord_home, ddtype)
  do i = 1, DIM
     global(i) = i_global(local(i), ddtype%n(i), coord_home(i))
     global(i) = i_periodic(global(i), ddtype%l(i))
  enddo

end

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

  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  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_dd
  integer, dimension(DIM)             :: coord
  integer                             :: i
  
  do i = 1, DIM
     ASSERT(global(i) >= 0)
     ASSERT(global(i) < ddtype%l(i))
     call i_home(global(i), ddtype%n(i), coord(i), local(i))
  enddo

  home = coord2pe_dd(coord,ddtype)

end

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

  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  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_dd(remote, coord_remote, ddtype)

  do i = 1, DIM
     ASSERT(global(i) >= 0)
     ASSERT(global(i) < ddtype%l(i))

     if (coord_remote(i) == 0 .and. global(i) == ddtype%l(i) - 1) then
        local(i) = -1
     elseif (coord_remote(i) == ddtype%ip(i) - 1 .and. global(i) == 0) then
        local(i) = ddtype%n(i)
     else
        local(i) = i_local(global(i), ddtype%n(i), coord_remote(i))
     endif

     ASSERT(local(i) >= -1)
     ASSERT(local(i) <= ddtype%n(i))
     if (ddtype%single(i)) local(i) = i_periodic(local(i), ddtype%n(i))
  enddo

end

!-------------------------------------------------------------------------------
integer function e_o_dd(local,me,ddtype)  !// returns EVEN or ODD (0 or 1)
  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  integer, intent(in) :: me
  integer, intent(in), dimension(DIM) :: local  ! local coordinates
  integer, dimension(DIM)             :: global ! global coordinates
  integer, external                   :: i_e_o

  call local2global_dd(me, local, global, ddtype)

  e_o_dd = i_e_o(DIM, global)
end

!-------------------------------------------------------------------------------
subroutine pe2coord_dd(pe, coord, ddtype)  ! returns coordinates of process pe
  use module_lattice
  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  integer, intent(in)                  :: pe
  integer, intent(out), dimension(DIM) :: coord

  call unlex_perm(pe, DIM, coord, ddtype%ip, process_mapping)
end

!-------------------------------------------------------------------------------
integer function coord2pe_dd(coord,ddtype)  ! returns process id
  use module_lattice
  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  integer, intent(in), dimension(DIM) :: coord
  integer, external                   :: ilex_perm

  coord2pe_dd = ilex_perm(DIM, coord, ddtype%ip, process_mapping)
end

!-------------------------------------------------------------------------------
subroutine my_coord_dd(me, coord, ddtype)  ! returns process coordinates

  use module_dd
  implicit none
  type(ddlatvol) :: ddtype
  integer, intent(out), dimension(DIM) :: coord
  integer :: me
  


  call pe2coord_dd(me, coord, ddtype)
end

!-------------------------------------------------------------------------------
subroutine coord_dd_test(me, ddtype)
  use module_dd
  implicit none
  type(ddlatvol) :: ddtype

  integer, external :: coord2pe_dd
  integer :: coord(DIM)
  integer :: pe,me


  call my_coord_dd(me,coord,ddtype)
  ASSERT(me == coord2pe_dd(coord,ddtype))

  do pe = 0, ddtype%num_ip - 1
     call pe2coord_dd(pe, coord, ddtype)
     ASSERT(pe == coord2pe_dd(coord, ddtype))
  enddo


  call open_diag()

  do pe = 0, ddtype%num_ip - 1
     call pe2coord_dd(pe, coord, ddtype)
     write(UDIAG,*) pe, coord
  enddo

end

!!!!-------------------------------------------------------------------------------
!!!subroutine ieo2local_dd(ieo, eo, local,me,ddtype)  ! returns local coords for given (ieo, eo)
!!!                                      ! (inverse of xyzt2i() and e_o())
!!!  use module_function_decl
!!!  use module_dd                       !//  provides N
!!!  implicit none
!!!  type(ddlatvol) :: ddtype
!!!  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, me
!!!  integer, intrinsic   :: ieor
!!!  integer, external    :: i_e_o, e_o_dd
!!!
!!!  ASSERT(ieo >= 1)
!!!  ASSERT(ieo <= ddtype%volh)
!!!
!!!  i = ieo - 1
!!!
!!!  call unlex(2 * i, DIM, local, ddtype%n)
!!!  local(1) =  local(1) &
!!!           + ieor( ieor(i_e_o(DIM - 1, local(2)), eo), e_o_dd((/0,0,0,0/),me,ddtype) )
!!!
!!!end
!!!
!!!!-------------------------------------------------------------------------------
!!!subroutine ieo2local_dd_test(me, ddtype)
!!!  use module_dd
!!!  implicit none
!!!  type(ddlatvol) :: ddtype
!!!  integer :: x, y, z, t
!!!  integer :: i, eo, me
!!!  integer :: local(DIM)
!!!  integer, external :: xyzt2i_dd, e_o_dd
!!!
!!!  do t = 0, ddtype%n(4) - 1
!!!  do z = 0, ddtype%n(3) - 1
!!!  do y = 0, ddtype%n(2) - 1
!!!  do x = 0, ddtype%n(1) - 1
!!!
!!!     i = xyzt2i_dd((/x, y, z, t/),  me,ddtype)
!!!     eo = e_o_dd((/x, y, z, t/),    me,ddtype)
!!!
!!!     call ieo2local_dd(i, eo, local,me,ddtype)
!!!
!!!     ASSERT(local(1) == x)
!!!     ASSERT(local(2) == y)
!!!     ASSERT(local(3) == z)
!!!     ASSERT(local(4) == t)
!!!  enddo
!!!  enddo
!!!  enddo
!!!  enddo
!!!
!!!end

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

!-------------------------------------------------------------------------------
      integer function n_sites_dd(dim, direction, n, single)

!     returns number of sites of local grid and boundaries
!     n_sites(dim, (/0, 0, ..., 0/), n, single) is the (local) grid volume

      implicit none
      integer dim, direction(dim), n(dim), d
      logical single(dim)

      n_sites_dd = 1
      do d = 1, dim
         if (direction(d) .eq. 0) then
            n_sites_dd = n_sites_dd * n(d)
         else
            if (single(d) ) then ! grid not partitioned in d-direction
               n_sites_dd = 0
               return
            endif
         endif
      enddo

      end

!-------------------------------------------------------------------------------
integer function e_o_gd(local)  !// returns EVEN or ODD (0 or 1)
  use module_dd
  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_dd(my_pe(), local, global, gd%lat)
  e_o_gd = i_e_o(DIM, global)
end
!-------------------------------------------------------------------------------
integer function e_o_ld(local,me)  !// returns EVEN or ODD (0 or 1)
  use module_dd
  use module_function_decl
  implicit none
  integer, intent(in)                 :: me
  integer, intent(in), dimension(DIM) :: local  ! local coordinates
  integer, dimension(DIM)             :: global, tmp
  integer, external                   :: i_e_o

  call local2global_dd(me,      local,    tmp, ld%lat)
  call local2global(my_pe(),   tmp, global)
  e_o_ld = i_e_o(DIM, global)
end
