!
! Description: Demonstrates using a local ordering to set values into a parallel vector
!
#include <petsc/finclude/petscvec.h>
program main
  use petscvec

  implicit none

  PetscErrorCode ierr
  PetscMPIInt rank
  PetscInt    ::   i, ng, rstart, rend, M
  PetscInt, pointer, dimension(:) :: gindices
  Vec   ::         x
  ISLocalToGlobalMapping :: ltog

  PetscCallA(PetscInitialize(ierr))
  PetscCallMPIA(MPI_Comm_rank(PETSC_COMM_WORLD, rank, ierr))

!
!     Create a parallel vector.
!      - In this case, we specify the size of each processor's local
!        portion, and PETSc computes the global size.  Alternatively,
!        PETSc could determine the vector's distribution if we specify
!        just the global size.
!
  PetscCallA(VecCreate(PETSC_COMM_WORLD, x, ierr))
  PetscCallA(VecSetSizes(x, rank + 1_PETSC_INT_KIND, PETSC_DECIDE, ierr))
  PetscCallA(VecSetFromOptions(x, ierr))

#if defined(PETSC_USE_COMPLEX)
  ! Fortran () automatically sets the complex KIND to correspond to the KIND of the constant arguments
  PetscCallA(VecSet(x, (1.0_PETSC_REAL_KIND, 0.0_PETSC_REAL_KIND), ierr))
  ! Alternatively one can set it explicitly using
  PetscCallA(VecSet(x, cmplx(1.0_PETSC_REAL_KIND, 0.0_PETSC_REAL_KIND, PETSC_REAL_KIND), ierr))
#else
  PetscCallA(VecSet(x, 1.0_PETSC_REAL_KIND, ierr))
  ! Alternatively one can set it explicitly using
  PetscCallA(VecSet(x, 1.0_PETSC_REAL_KIND, ierr))
#endif
!
!     Set the local to global ordering for the vector. Each processor
!     generates a list of the global indices for each local index. Note that
!     the local indices are just whatever is convenient for a particular application.
!     In this case we treat the vector as lying on a one dimensional grid and
!     have one ghost point on each end of the blocks owned by each processor.
!

  PetscCallA(VecGetSize(x, M, ierr))
  PetscCallA(VecGetOwnershipRange(x, rstart, rend, ierr))
  ng = rend - rstart + 2
  allocate (gindices(0:ng - 1))
  gindices(0) = rstart - 1

  do i = 0, ng - 2
    gindices(i + 1) = gindices(i) + 1
  end do

! map the first and last point as periodic

  if (gindices(0) == -1) gindices(0) = M - 1

  if (gindices(ng - 1) == M) gindices(ng - 1) = 0

  PetscCallA(ISLocalToGlobalMappingCreate(PETSC_COMM_SELF, 1_PETSC_INT_KIND, ng, gindices, PETSC_COPY_VALUES, ltog, ierr))
  PetscCallA(VecSetLocalToGlobalMapping(x, ltog, ierr))
  PetscCallA(ISLocalToGlobalMappingDestroy(ltog, ierr))
  deallocate (gindices)

  ! Set the vector elements.
  ! - In this case set the values using the local ordering
  ! - Each processor can contribute any vector entries,
  !   regardless of which processor "owns" them; any nonlocal
  !   contributions will be transferred to the appropriate processor
  !   during the assembly process.
  ! - In this example, the flag ADD_VALUES indicates that all
  !   contributions will be added together.

  do i = 0, ng - 1
#if defined(PETSC_USE_COMPLEX)
    PetscCallA(VecSetValuesLocal(x, 1_PETSC_INT_KIND, [i], [(1.0_PETSC_REAL_KIND, 0.0_PETSC_REAL_KIND)], ADD_VALUES, ierr))
#else
    PetscCallA(VecSetValuesLocal(x, 1_PETSC_INT_KIND, [i], [1.0_PETSC_REAL_KIND], ADD_VALUES, ierr))
#endif
  end do

  !
  ! Assemble vector, using the 2-step process:
  ! VecAssemblyBegin(), VecAssemblyEnd()
  ! Computations can be done while messages are in transition
  ! by placing code between these two statements.
  !
  PetscCallA(VecAssemblyBegin(x, ierr))
  PetscCallA(VecAssemblyEnd(x, ierr))
  !
  ! View the vector; then destroy it.
  !
  PetscCallA(VecView(x, PETSC_VIEWER_STDOUT_WORLD, ierr))
  PetscCallA(VecDestroy(x, ierr))
  PetscCallA(PetscFinalize(ierr))

end program

!/*TEST
!
!     test:
!       nsize: 4
!       output_file: output/ex8_1.out
!
!TEST*/
