!> \~japanese
!! \brief 
!!
!! \par LocalProcRect_Manager モジュールについて
!!
!! \~english
!! \brief 
!!
!! \par About LocalProcRect_Manager module
!!
!! \~
!! \since IGMBaseLib 2.0
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b License ?? <br>
!! \author Yuta Kawai
!!
module LocalProcRect_Manager
 
  ! モジュール引用 ; Use statements
  !

  ! Message Passing Interface
  !
  !
#ifdef LIB_MPI
  use mpi
#endif
  
  ! 宣言文 ; Declaration statements
  !
  implicit none
  private
  
  !
  !
  !
  type, public :: LocalProcRectInfo
    
    logical :: MPI_flag
    integer :: lcproc_MPIRank
    integer :: rlevel
    
    integer :: iEMin
    integer :: iEMax
    integer :: jEMin
    integer :: jEMax
    integer :: iIdMin
    integer :: iIdMax
    integer :: jIdMin
    integer :: jIdMax
    integer :: globalRCID

    integer :: globalRCIDMin    
    integer :: globalRCIDMax

  end type LocalProcRectInfo

  !
  !
  integer :: MAX_RLEVEL = 10

  ! 公開手続き
  ! Public procedures
  !
  public :: LocalProcRectInfo_Init, LocalProcRectInfo_Final
  public :: calc_LcProcRC_IndexInfo, calc_LcProcRC_MPIRank
  public :: get_LcProcRCUp_MPIRank, get_LcProcRCLow_MPIRank
  public :: get_LcProcRCLeft_MPIRank, get_LcProcRCRight_MPIRank
  public :: get_InteriorRegion_IndexInfo
  
  ! 非公開変数
  ! Private variables
  !
  integer :: glRCIDList_NSphere(0:6) = (/ 5, 1, 2, 3, 4, 5, 1 /)
  integer :: glRCIDList_SSphere(0:6) = (/ 10, 6, 7, 8, 9, 10, 6 /)

contains
subroutine LocalProcRectInfo_Init( &
  & self,                           & ! (inout) 
  & globalRC_EMin, globalRC_EMax,   & ! (in)  
  & globalRC_idMin, globalRC_idMax, & ! (in)
  & MPI_flag                        & ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(inout) :: self
  integer, intent(in) :: globalRC_EMin  
  integer, intent(in) :: globalRC_EMax
  integer, intent(in) :: globalRC_idMin  
  integer, intent(in) :: globalRC_idMax
  logical, intent(in) :: MPI_flag
  
  ! 作業変数
  ! Work variables
  !
  integer :: rcID
  integer :: ierr
  
  ! 実行文 ; Executable statements
  !

  if (MPI_flag) then

    ! MPI による並列化が有効な場合
    !
  		
#ifdef LIB_MPI
    call set_LocalProcRCInfo( &
     & self, globalRC_EMin, globalRC_EMax, globalRC_idMin, globalRC_idMax )
#endif

  else
    
    ! MPI による並列化が無効な場合
    !

    self%MPI_flag = .false.
    self%lcproc_MPIrank = -1
    self%rlevel = 0

    self%iEMin = globalRC_EMin
    self%iEMax = globalRC_EMax
    self%jEMin = globalRC_EMin
    self%jEMax = globalRC_EMax
    self%iIdMin = globalRC_IdMin
    self%iIdMax = globalRC_IdMax
    self%jIdMin = globalRC_IdMin
    self%jIdMax = globalRC_IdMax

    self%globalRCIDMin = 1
    self%globalRCIDMax = 10

  end if
   
end subroutine LocalProcRectInfo_Init

subroutine LocalProcRectInfo_Final( &
  & self                            & ! (inout)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(inout) :: self
	
  
end subroutine LocalProcRectInfo_Final

function calc_LcProcRC_MPIRank( &
  & rlevel,                     & ! (in) 
  & rcID, lcpc_i, lcpc_j        & ! (in)
  & ) result(lcpcRC_MPI_rank)

  ! 宣言文 ; Declaration statements
  !
  integer, intent(in) :: rlevel
  integer, intent(in) :: rcID
  integer, intent(in) :: lcpc_i
  integer, intent(in) :: lcpc_j
  integer :: lcpcRC_MPI_rank

  ! 実行文 ; Executable statements
  !
  
  lcpcRC_MPI_rank =   ( rcID - 1 ) * 4**rlevel &
       &               + ( lcpc_i - 1 ) * 2**rlevel + lcpc_j - 1
		
end function calc_LcProcRC_MPIRank

subroutine get_InteriorRegion_IndexInfo( &
  & self,                        & ! (in)
  & iEMin, iEMax, jEMin, jEMax,  & ! (out)
  & RCIDMin, RCIDMax             & ! (out)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(in) :: self
  integer, intent(out) :: iEMin
  integer, intent(out) :: iEMax
  integer, intent(out) :: jEMin
  integer, intent(out) :: jEMax
  integer, intent(out) :: RCIDMin
  integer, intent(out) :: RCIDMax

  ! 実行文 ; Executable statements
  !

  iEMin = self%iEMin
  iEMax = self%iEMax
  jEMin = self%jEMin
  jEMax = self%jEMax
  RCIDMin = self%globalRCIDMin
  RCIDMax = self%globalRCIDMax

end subroutine get_InteriorRegion_IndexInfo

subroutine calc_LcProcRC_IndexInfo( &
  & my_rank, rlevel,         &
  & rcID, lcpcRC_i, lcpcRC_j &
  & ) 

  ! 宣言文 ; Declaration statements
  !  
  integer, intent(in) :: my_rank
  integer, intent(in) :: rlevel
  integer, intent(out) :: rcID
  integer, intent(out) :: lcpcRC_i
  integer, intent(out) :: lcpcRC_j

  ! 作業変数
  ! Work variables
  !	
  integer :: EachRC_MPIRankBase, EachRC_myRank

  ! 実行文 ; Executable statements
  !
 	
  rcID = int(my_rank / 4**rlevel) + 1
  EachRC_MPIRankBase = ( rcID - 1 ) * 4**rlevel
  EachRC_myRank = my_rank - EachRC_MPIRankBase + 1

  lcpcRC_i = int(EachRC_myRank / (2**rlevel+1)) + 1
  lcpcRC_j = EachRC_myRank - ( lcpcRC_i - 1 ) * 2**rlevel

end subroutine calc_LcProcRC_IndexInfo

function get_LcProcRCLeft_MPIRank( &
  & self &
  & ) result(ret_MPIRank)

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(in) :: self
  integer :: ret_MPIRank

  ! 作業変数
  ! Work variables
  !
  integer :: myRCID, myLcRC_i, mylcRC_j, myRCID_dum
  integer :: retLcRC_glRCID, retLcRC_i, retLcRC_j

  ! 実行文 ; Executable statements
  !

  call calc_LcProcRC_IndexInfo( &
    & self%lcproc_MPIRank, self%rlevel, &
    & myRCID, myLcRC_i, myLcRC_j &
    & )
  
  retLcRC_i = myLcRC_i 
  retLcRC_j = myLcRC_j - 1
  retLcRC_glRCID = myRCID
    
  if ( retLcRC_j == 0 ) then
  
    if ( myRCID <= 5 ) then
      myRCID_dum = myRCID
    else
      myRCID_dum = myRCID - 5
    end if
    
    if ( myRCID <= 5 ) then
      retLcRC_glRCID = glRCIDList_NSphere(myRCID_dum - 1)
      retLcRC_i = myLcRC_j
      retLcRC_j = myLcRC_i
    else
      retLcRC_glRCID = glRCIDList_NSphere(myRCID_dum)
      retLcRC_i = myLcRC_i
      retLcRC_j = 2**self%rlevel
    end if
  end if
    
  ret_MPIRank = calc_LcProcRC_MPIRank(self%rlevel, retLcRC_glRCID, retLcRC_i, retLcRC_j)
  
end function get_LcProcRCLeft_MPIRank

function get_LcProcRCLow_MPIRank( &
  & self &
  & ) result(ret_MPIRank)

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(in) :: self
  integer :: ret_MPIRank

  ! 作業変数
  ! Work variables
  !
  integer :: myRCID, myLcRC_i, mylcRC_j, myRCID_dum
  integer :: retLcRC_glRCID, retLcRC_i, retLcRC_j

  ! 実行文 ; Executable statements
  !

  call calc_LcProcRC_IndexInfo( &
    & self%lcproc_MPIRank, self%rlevel, &
    & myRCID, myLcRC_i, myLcRC_j &
    & )
  
  retLcRC_i = myLcRC_i + 1
  retLcRC_j = myLcRC_j 
  retLcRC_glRCID = myRCID
 
  if ( retLcRC_i > 2**self%rlevel ) then
  
    if ( myRCID <= 5 ) then
      myRCID_dum = myRCID
    else
      myRCID_dum = myRCID - 5
    end if

    if ( myRCID <= 5 ) then
      retLcRC_glRCID = glRCIDList_SSphere(myRCID_dum - 1)
      retLcRC_i = 1
      retLcRC_j = myLcRC_j
    else
      retLcRC_glRCID = glRCIDList_SSphere(myRCID_dum - 1)
      retLcRC_i = myLcRC_j
      retLcRC_j = myLcRC_i
    end if
  end if
  
  ret_MPIRank = calc_LcProcRC_MPIRank(self%rlevel, retLcRC_glRCID, retLcRC_i, retLcRC_j)

end function get_LcProcRCLow_MPIRank


function get_LcProcRCRight_MPIRank( &
  & self &
  & ) result(ret_MPIRank)

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(in) :: self
  integer :: ret_MPIRank

  ! 作業変数
  ! Work variables
  !
  integer :: myRCID, myLcRC_i, mylcRC_j, myRCID_dum
  integer :: retLcRC_glRCID, retLcRC_i, retLcRC_j

  ! 実行文 ; Executable statements
  !

  call calc_LcProcRC_IndexInfo( &
    & self%lcproc_MPIRank, self%rlevel, &
    & myRCID, myLcRC_i, myLcRC_j &
    & )
  
  retLcRC_i = myLcRC_i
  retLcRC_j = myLcRC_j + 1
  retLcRC_glRCID = myRCID
   
  if ( retLcRC_j > 2**self%rlevel ) then
    
    if ( myRCID <= 5 ) then
      myRCID_dum = myRCID
    else
      myRCID_dum = myRCID - 5
    end if

  
    if ( myRCID <= 5 ) then
      retLcRC_glRCID = glRCIDList_SSphere(myRCID_dum)
      retLcRC_i = myLcRC_i
      retLcRC_j = 1
    else
      retLcRC_glRCID = glRCIDList_SSphere(myRCID_dum + 1)
      retLcRC_i = myLcRC_j
      retLcRC_j = myLcRC_i
    end if
  end if
  
  ret_MPIRank = calc_LcProcRC_MPIRank(self%rlevel, retLcRC_glRCID, retLcRC_i, retLcRC_j)

end function get_LcProcRCRight_MPIRank


function get_LcProcRCUp_MPIRank( &
  & self &
  & ) result(ret_MPIRank)

  ! 宣言文 ; Declaration statements
  !
  type(LocalProcRectInfo), intent(in) :: self
  integer :: ret_MPIRank

  ! 作業変数
  ! Work variables
  !
  integer :: myRCID, myLcRC_i, mylcRC_j, myRCID_dum
  integer :: retLcRC_glRCID, retLcRC_i, retLcRC_j

  ! 実行文 ; Executable statements
  !

  call calc_LcProcRC_IndexInfo( &
    & self%lcproc_MPIRank, self%rlevel, &
    & myRCID, myLcRC_i, myLcRC_j &
    & )
  
  retLcRC_i = myLcRC_i - 1
  retLcRC_j = myLcRC_j 
  retLcRC_glRCID = myRCID
   
  if ( retLcRC_i == 0 ) then
    
    if ( myRCID <= 5 ) then
      myRCID_dum = myRCID
    else
      myRCID_dum = myRCID - 5
    end if

    if ( myRCID <= 5 ) then
      retLcRC_glRCID = glRCIDList_NSphere(myRCID_dum + 1)
      retLcRC_i = myLcRC_j
      retLcRC_j = myLcRC_i
    else
      retLcRC_glRCID = glRCIDList_NSphere(myRCID_dum + 1)
      retLcRC_i = 2**self%rlevel
      retLcRC_j = myLcRC_j
    end if
  end if
  
  ret_MPIRank = calc_LcProcRC_MPIRank(self%rlevel, retLcRC_glRCID, retLcRC_i, retLcRC_j)

end function get_LcProcRCUp_MPIRank

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! 非公開手続き
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#ifdef LIB_MPI
subroutine set_LocalProcRCInfo( &
  & self, &
  & globalRC_EMin, globalRC_EMax, &
  & globalRC_idMin, globalRC_idMax &
  & ) 

  ! 宣言文 ; Declaration statements
  !	
  type(LocalProcRectInfo), intent(inout) :: self
  integer, intent(in) :: globalRC_EMin
  integer, intent(in) :: globalRC_EMax
  integer, intent(in) :: globalRC_idMin
  integer, intent(in) :: globalRC_idMax

  ! 作業変数
  ! Work variables
  !
  integer :: ierr
  integer :: mpi_size, my_rank
  integer :: lcproc_glRCIdMin, lcproc_glRCIdMax, lcproc_glRCNUM
  integer :: rcID, rlevel, EachRC_lcproc_num
  integer :: EachRC_MPIRankBase, EachRC_myRank
  integer :: myLcRC_i, myLcRC_j, myLcRC_effsize
  integer :: halo_size


  ! 実行文 ; Executable statements
  !

  call MPI_Comm_rank(MPI_COMM_WORLD, my_rank, ierr)
  call MPI_Comm_size(MPI_COMM_WORLD, mpi_size, ierr)

  self%MPI_flag = .true.
  self%lcproc_MPIRank = my_rank
  halo_size = globalRC_EMin - globalRC_idMin
  
  if ( mpi_size < 10 ) then

    write(*,*) 'MPI size must be larger than or equal 10.'
    stop
 
  else

    rlevel = check_valid_rLevel(mpi_size)
    if ( rlevel == -1 ) then
      write(*,*) 'When MPI size is large than 10, &
          & 10 * 4^rlevel must be set to MPI size'
      stop
    end if

    self%rlevel = rlevel
    EachRC_lcproc_num = mpi_size / 10

    call calc_LcProcRC_IndexInfo( &
      & my_rank, rlevel,                    & ! (in)
      & self%globalRCID, myLcRC_i, myLcRC_j & ! (out)
       & )
        
    self%globalRCIDMin = self%globalRCID
    self%globalRCIDMax = self%globalRCID
    
    myLcRC_effsize = int((globalRC_EMax - globalRC_EMin + 1) / 2**rlevel )+1

    self%iEMin = myLcRC_effsize * ( myLcRC_i - 1 ) + 1
    self%iEMax = myLcRC_effsize * myLcRC_i
    self%jEMin = myLcRC_effsize * ( myLcRC_j - 1 ) + 1
    self%jEMax = myLcRC_effsize * myLcRC_j

    if ( myLcRC_i == 2**rlevel ) self%iEMax = globalRC_EMax
    if ( myLcRC_j == 2**rlevel ) self%jEMax = globalRC_EMax

    self%iIdMin = self%iEMin - halo_size
    self%iIdMax = self%iEMax + halo_size
    self%jIdMin = self%jEMin - halo_size
    self%jIdMax = self%jEMax + halo_size

  end if

end subroutine set_LocalProcRCInfo
#endif

function check_valid_rLevel( &
  & mpi_size &
  & ) result(rlevel)
  
  ! 宣言文 ; Declaration statements
  !  
  integer, intent(in) :: mpi_size
  integer :: rlevel

  ! 作業変数
  ! Work variables
  !
  integer :: rlevelID

  ! 実行文 ; Executable statements
  !

  rlevel = -1

  if ( mpi_size < 10 ) then
      return
  end if

  do rlevelID=0, MAX_RLEVEL
     if ( mpi_size == 10 * 4**rlevelID ) then
        rlevel = rlevelID
        return
     end if
  end do

   rlevel = -1

end function check_valid_rlevel

end module LocalProcRect_Manager
