!
! Copyright (C) by Argonne National Laboratory
!     See COPYRIGHT in top-level directory
!

!
! In MPI 2.2, the behavior of attributes set in one language and retrieved
! from another was defined.  There are three types of attribute values:
!  1. pointer (void *): "The C type"
!  2. INTEGER         : "The MPI-1 Fortran type"
!  3. INTEGER (KIND=MPI_ADDRESS_KIND) : "The MPI-2 Fortran type"
!
! All three apply to Communicator attributes, with case 2 using the
! deprecated MPI_ATTR_GET and MPI_ATTR_PUT routines. For Datatype and
! RMA Window attributes, cases 1 and 3 apply.
!
! Note, just to make this more complex, there are some reasons why an MPI
! implementation may choose to make MPI_Aint (and the corresponding
! Fortran MPI_ADDRESS_KIND) larger than a void pointer.  Specifically,
! make it as large as MPI_Offset, which simplifies certain operations
! with datatypes.
!
! There are 9 cases:
! 1. C sets, C gets
! 2. C sets, Fortran INTEGER gets
! 3. C sets, Fortran ADDRESS_KIND gets
! 4. Fortran INTEGER sets, C gets
! 5. Fortran INTEGER sets, Fortran INTEGER gets
! 6. Fortran INTEGER sets, Fortran ADDRESS_KIND gets
! 7. Fortran ADDRESS_KIND sets, C gets
! 8. Fortran ADDRESS_KIND sets, Fortran INTEGER gets
! 9. Fortran ADDRESS_KIND sets, Fortran ADDRESS_KIND gets
!
! These are the basic cases.  They are complicated by the fact that
! the attribute values have 3 types: void * (C interface), MPI_Fint
! (Fortran INTEGER), and MPI_Aint (Fortran ADDRESS_KIND).  These
! have the following size relationships:
!
! sizeof(void *) <= sizeof(MPI_Aint)
!     (For some systems, MPI_Aint is set to the same size as
!      MPI_Offset, and may be larger than a void *.)
! sizeof(MPI_Fint) <= sizeof(MPI_Aint)
!     (Not strictly defined, but all reasonable implementations will
!      have this property)
!
! When a value is stored, the full value is stored (this may be fewer
! bytes than the maximum-sized attribute, in which case the high
! bytes are stored as zero).  When a value is retrieved, if the
! destination location is smaller, the low bytes (in value) are
! saved; this is the same as trunction.  If the destination location
! is longer, then then value is sign-extended (See MPI-3, 17.2.7).
!
! Specifically, if the value was set from Fortran, C will return a
! pointer to the appropriate-sized integer.
! When the value is set from C but accessed from Fortran, the value
! is converted to an integer of the appropriate length, possibly truncated.
!
! FIXME: The above different-length attribute case is not yet handled
!  in this code.
!
! In addition to setting and getting attributes, they are accessed
! through duplication (COMM_DUP and TYPE_DUP), and on deletion of the
! object to which they are attached, when the copy functions will be
! well-defined.
!
! This code was inspired by a code written by Jeff Squyres to test these
! nine cases. This code, however, is different.
!
! So, for each of the same->same tests:
!    Store largest positive and negative attributes.  Dup them,
!    retrieve them, delete them.  All bytes should remain value, and
!    no other.  Use keys created in all three languages for set/get;
!    use language under test for dup.
!
! For X->Y tests:
!    Using X, store into key created in all three.
!    Using Y, retrieve all attributes.  See above for handling of
!     truncated or sign-extended
!
! Use Fortran to drive tests (Fortran main program).  Call C for
! C routines and to check data with different sizes (to ensure that
! the proper bytes are used in the value).
!
! Use the same keyval for attributes used in both C and Fortran (both
! modes).  This found an error in MPICH, where the type of the
! attribute (e.g., pointer, integer, or address-sized integer) needs
! to be saved.
!
! Module containing the keys and other information, including
! interfaces to the C routines
      module keyvals
        use mpi_f08
        logical fverbose, useintSize
        integer ptrSize, intSize, aintSize
        integer fcomm1_key, fcomm1_extra
        integer fcomm2_key, ftype2_key, fwin2_key
        integer ccomm1_key
        integer ccomm2_key, ctype2_key, cwin2_key
        TYPE(MPI_Win) win
        integer (kind=MPI_ADDRESS_KIND) fcomm2_extra, ftype2_extra,&
             & fwin2_extra
        interface
           pure function bigint()
             integer bigint
           end function bigint
           pure function bigaint()
             use mpi_f08, only : MPI_ADDRESS_KIND
             integer (kind=MPI_ADDRESS_KIND) bigaint
           end function bigaint
           ! Could use bind(c) once we require that level of Fortran support.
           subroutine csetmpi( fcomm, fkey, val, errs )
             use mpi_f08
             TYPE(MPI_Comm), INTENT(IN) :: fcomm
             integer, INTENT(IN) :: fkey, val
             integer errs
           end subroutine csetmpi
           subroutine csetmpi2( fcomm, fkey, val, errs )
             use mpi_f08
             TYPE(MPI_Comm), INTENT(IN) :: fcomm
             integer, INTENT(IN) :: fkey
             integer  errs
             integer (KIND=MPI_ADDRESS_KIND) val
           end subroutine csetmpi2
           subroutine cattrinit( fv )
             integer fv
           end subroutine cattrinit
           subroutine cgetsizes( ps, is, as )
             integer, INTENT(OUT) :: ps, is, as
           end subroutine cgetsizes
           subroutine ccreatekeys( k1, k2, k3, k4 )
             integer, INTENT(OUT) :: k1, k2, k3, k4
           end subroutine ccreatekeys
           subroutine cfreekeys()
           end subroutine cfreekeys
           subroutine ctoctest( errs )
             integer errs
           end subroutine ctoctest
        end interface
      end module keyvals
!
      program main
      use mpi_f08
      use keyvals
      implicit none
      integer ierr
      integer errs, tv, rank
      integer(MPI_ADDRESS_KIND) tmp

      errs = 0
      call MTEST_INIT( ierr )
      call MPI_COMM_RANK( MPI_COMM_WORLD, rank, ierr )
!
!     Let the C routines know about debugging
      call cgetenvbool( "MPITEST_VERBOSE", tv )
      if (tv .eq. 1) then
         fverbose = .true.
         call cattrinit( 1 )
      else
         fverbose = .false.
         call cattrinit( 0 )
      endif
!
! If this value is true, define an "big MPI_Aint" value that fits in
! an MPI_Fint (see function bigaint)
      call cgetenvbool( "MPITEST_ATTR_INTFORAINT", tv )
      if (tv .eq. 1) then
         useintSize = .true.
      else
         useintSize = .false.
      endif
!
!     Get the sizes of the three types of attributes
      call cgetsizes( ptrSize, intSize, aintSize )
      if (fverbose) then
         print *, 'sizeof(ptr)=',ptrSize, ' sizeof(int)=', intSize, ' &
              &sizeof(aint)=', aintSize
      endif
!
!     Create the keyvals
!
!     Create the attribute values to use.  We want these to use the full
!     available width, which depends on both the type and the test,
!     since when switching between types of different sizes, we need to
!     check only the "low" bits (those shared in types of each size).
!
!
      if (fverbose) then
         print *, "Creating Fortran attribute keys"
      endif

      call fCreateKeys()
      if (fverbose) then
         print *, "Creating C attribute keys"
      endif
      call ccreatekeys( ccomm1_key, ccomm2_key, ctype2_key, cwin2_key&
           & )
!
!     Create a window to use with the attribute tests in Fortran
      tmp = 0
      call MPI_WIN_CREATE( MPI_BOTTOM, tmp, 1, MPI_INFO_NULL,&
           & MPI_COMM_WORLD, win, ierr )
!
      if (fverbose) then
         print *, "Case 1: C sets and C gets"
      endif
      call ctoctest( errs )
      if (fverbose) then
         print *, "Case 2: C sets and Fortran (MPI1) gets"
      endif
      call ctof1test( errs )
      if (fverbose) then
         print *, "Case 3: C sets and Fortran (MPI2) gets"
      endif
      call ctof2test( errs )
      if (fverbose) then
         print *, "Case 4: Fortran (MPI1) sets and C gets"
      endif
      call f1toctest( errs )
      if (fverbose) then
         print *, "Case 5: Fortran (MPI1) sets and gets"
      endif
      call f1tof1test( errs )
      if (fverbose) then
         print *, "Case 6: Fortran (MPI1) sets and Fortran (MPI2) gets"
      endif
      call f1tof2test( errs )
      if (fverbose) then
         print *, "Case 7: Fortran (MPI2) sets and C gets"
      endif
      call f2toctest( errs )
      if (fverbose) then
         print *, "Case 8: Fortran (MPI2) sets and Fortran (MPI1) gets"
      endif
      call f2tof1test( errs )
      if (fverbose) then
         print *, "Case 9: Fortran (MPI2) sets and gets"
      endif
      call f2tof2test( errs )

! Cleanup
      call ffreekeys()
      call cfreekeys()
      call MPI_WIN_FREE( win, ierr )

      call MPI_REDUCE( MPI_IN_PLACE, errs, 1, MPI_INT, MPI_SUM, 0,&
           & MPI_COMM_WORLD, ierr )

      call MTEST_FINALIZE( errs )

      end
!
! -------------------------------------------------------------------
! Check attribute set in Fortran (MPI-1) and read from Fortran (MPI-1)
! -------------------------------------------------------------------
      integer function FMPI1checkCommAttr( comm, key, expected, msg )
      use mpi_f08
      integer key, expected
      TYPE(MPI_Comm) comm
      character*(*) msg
      integer value, ierr
      logical flag
!
      FMPI1checkCommAttr = 0
      call MPI_ATTR_GET( comm, key, value, flag, ierr )
      if (.not. flag) then
         print *, "Error: reading Fortran INTEGER attribute: ", msg
         FMPI1checkCommAttr = 1
         return
      endif
      if (value .ne. expected) then
         print *, "Error: Fortran INTEGER attribute: ", msg
         print *, "Expected ", expected, " but got ", value
         FMPI1checkCommAttr = 1
      endif
      return
      end
!
! -------------------------------------------------------------------
! Functions associated with attribute copy/delete.
! -------------------------------------------------------------------
      subroutine FMPI1_COPY_FN( oldcomm, key, extrastate, inval,&
           & outval, flag, ierr )
      use mpi_f08
      use keyvals, only : fverbose
      integer key, extrastate, inval, outval, ierr
      TYPE(MPI_Comm) oldcomm
      logical flag
!
      if (fverbose) then
         print *, 'FMPI1_COPY: Attr in = ', inval, ' extra = ',&
              & extrastate
      endif
      flag = .true.
      outval = inval + 1
      ierr = MPI_SUCCESS
      end
!
      subroutine FMPI1_DELETE_FN( oldcomm, key, extrastate, inval,&
     &     ierr )
      use mpi_f08
      use keyvals, only : fverbose
      integer key, extrastate, inval, ierr
      TYPE(MPI_Comm) oldcomm
      logical flag
!
      if (fverbose) then
         print *, "FMPI1_DELETE: inval = ", inval, " extra = ",&
              & extrastate
      endif
      ierr = MPI_SUCCESS
      end
!
!
      subroutine FMPI2_COPY_FN( oldcomm, key, extrastate, inval, outval,&
     &     flag, ierr )
      use mpi_f08
      use keyvals, only : fverbose
      implicit none
      integer key, ierr
      TYPE(MPI_Comm) oldcomm
      integer (KIND=MPI_ADDRESS_KIND) inval, outval, extrastate
      logical flag
!
      if (fverbose) then
         print *, 'FMPI2_COPY: Attr in = ', inval, ' extra = ',&
              & extrastate
      endif
      flag = .true.
      outval = inval + 1
      ierr = MPI_SUCCESS
      end
!
      subroutine FMPI2_DELETE_FN( oldcomm, key, extrastate, inval,&
     &     ierr )
      use mpi_f08
      use keyvals, only : fverbose
      implicit none
      integer key, ierr
      TYPE(MPI_Comm) oldcomm
      integer (kind=MPI_ADDRESS_KIND) inval, extrastate
!
      if (fverbose) then
         print *, "FMPI2_DELETE: inval = ", inval, " extra = ",&
              & extrastate
      endif
      ierr = MPI_SUCCESS
      end
! -------------------------------------------------------------------
!
! Typical check pattern
!
! Set value
! Get value (check set in same form)
! Get value in other modes
! Dup object (updates value)
! Get value in other modes
! Delete dup'ed object; check correct value sent to delete routine
!
      subroutine fcreateKeys( )
        use mpi_f08
        use keyvals
        implicit none
        external FMPI1_COPY_FN, FMPI1_DELETE_FN, FMPI2_COPY_FN,&
           & FMPI2_DELETE_FN
        integer ierr

        fcomm1_extra = 0
        fcomm2_extra = 0
        ftype2_extra = 0
        fwin2_extra  = 0
        call MPI_KEYVAL_CREATE( FMPI1_COPY_FN, FMPI1_DELETE_FN,&
             & fcomm1_key, fcomm1_extra, ierr )
        call MPI_COMM_CREATE_KEYVAL( FMPI2_COPY_FN, FMPI2_DELETE_FN,&
             & fcomm2_key, fcomm2_extra, ierr )
        call MPI_TYPE_CREATE_KEYVAL( FMPI2_COPY_FN, FMPI2_DELETE_FN,&
             & ftype2_key, ftype2_extra, ierr )
        call MPI_WIN_CREATE_KEYVAL( FMPI2_COPY_FN, FMPI2_DELETE_FN,&
             & fwin2_key, fwin2_extra, ierr )

      end subroutine fcreateKeys
!
      subroutine ffreekeys()
        use mpi_f08
        use keyvals
        implicit none
        integer ierr

        call MPI_KEYVAL_FREE( fcomm1_key, ierr )
        call MPI_COMM_FREE_KEYVAL( fcomm2_key, ierr )
        call MPI_TYPE_FREE_KEYVAL( ftype2_key, ierr )
        call MPI_WIN_FREE_KEYVAL( fwin2_key, ierr )

        return
      end subroutine ffreekeys
! -------------------------------------------------------------------
! Set attributes in Fortran (MPI-1) and read them from Fortran (MPI-1)
      subroutine f1tof1test(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr, attrval, fcomm1attr
        TYPE(MPI_Comm) fdup
!
        fcomm1attr = bigint()
        attrval    = fcomm1attr
        call MPI_ATTR_PUT( MPI_COMM_SELF, fcomm1_key, fcomm1attr,&
             & ierr )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, &
             &"F to F (check value)", errs )
        call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
        call fmpi1read( fdup, fcomm1_key, attrval + 1, "F to F dup",&
             & errs )
        call MPI_COMM_FREE( fdup, ierr )
!
! Use a negative value
        fcomm1attr = -bigint()
        attrval    = fcomm1attr
        call MPI_ATTR_PUT( MPI_COMM_SELF, fcomm1_key, fcomm1attr,&
             & ierr )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, &
             &"F to F (check neg value)", errs )
        call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
        call fmpi1read( fdup, fcomm1_key, attrval + 1, "F to F dup",&
             & errs )
        call MPI_COMM_FREE( fdup, ierr )

      end subroutine f1tof1test
!
! Set attributes in C and read them from Fortran (MPI-1)
      subroutine ctof1test(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr, attrval, fcomm1attr
        TYPE(MPI_Comm) fdup
!
        fcomm1attr = bigint()
        attrval    = fcomm1attr
        call csetmpi( MPI_COMM_SELF, fcomm1_key, fcomm1attr, errs )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, "C to F",&
             & errs )
        if (ptrSize .eq. intSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call fmpi1read( fdup, fcomm1_key, attrval + 1, "C to F dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        fcomm1attr = -bigint()
        attrval    = fcomm1attr
        call csetmpi( MPI_COMM_SELF, fcomm1_key, fcomm1attr, errs )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, "C to F",&
             & errs )
        if (ptrSize .eq. intSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call fmpi1read( fdup, fcomm1_key, attrval + 1, "C to F dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif

      end subroutine ctof1test
!
! Set attributes in Fortran (MPI-1) and read in Fortran (MPI-2)
      subroutine f1tof2test(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr, attrval, fcomm1attr
        TYPE(MPI_Comm) fdup
        integer (kind=MPI_ADDRESS_KIND) expected
!
        fcomm1attr = bigint()
        attrval    = fcomm1attr
        call MPI_ATTR_PUT( MPI_COMM_SELF, fcomm1_key, fcomm1attr,&
             & ierr )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, &
             & "F to F (check value for F2 test)", errs )
        if (intSize .eq. aintSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           expected = attrval + 1
           call fmpi2read( fdup, fcomm1_key, expected, "F to F2 dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        fcomm1attr = -bigint()
        attrval    = fcomm1attr
        call MPI_ATTR_PUT( MPI_COMM_SELF, fcomm1_key, fcomm1attr,&
             & ierr )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, &
             &"F to F (check neg value for F2 test)", errs )
        if (intSize .eq. aintSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           expected = attrval + 1
           call fmpi2read( fdup, fcomm1_key, expected, "F to F2 dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif

      end subroutine f1tof2test
!
      subroutine fmpi1read( comm, key, expected, msg, errs )
        use mpi_f08
        implicit none
        integer key, expected, errs
        TYPE(MPI_Comm) comm
        character *(*) msg
        logical flag
        integer ierr, attrval
!
        call MPI_ATTR_GET( comm, key, attrval, flag, ierr )
        if (.not. flag) then
           print *, 'Error: flag false for Attr_get: ', msg
           errs = errs + 1
           return
        endif
        if (attrval .ne. expected) then
           print *, 'Error: expected ', expected, ' but saw ',&
                & attrval, ':', msg
           errs = errs + 1
        endif
        return
      end subroutine fmpi1read

      subroutine fmpi2read( comm, key, expected, msg, errs )
        use mpi_f08
        implicit none
        integer key, errs
        TYPE(MPI_Comm) comm
        integer (kind=MPI_ADDRESS_KIND) expected
        character *(*) msg
        logical flag
        integer ierr
        integer (kind=MPI_ADDRESS_KIND) attrval
!
        call MPI_COMM_GET_ATTR( comm, key, attrval, flag, ierr )
        if (.not. flag) then
           print *, 'Error: flag false for Attr_get: ', msg
           errs = errs + 1
           return
        endif
        if (attrval .ne. expected) then
           print *, 'Error: expected ', expected, ' but saw ',&
                & attrval, ':', msg
           errs = errs + 1
        endif
        return
      end subroutine fmpi2read

      subroutine fmpi2readwin( win, key, expected, msg, errs )
        use mpi_f08
        implicit none
        integer key, errs
        TYPE(MPI_Win) win
        integer (kind=MPI_ADDRESS_KIND) expected
        character *(*) msg
        logical flag
        integer ierr
        integer (kind=MPI_ADDRESS_KIND) attrval
!
        call MPI_WIN_GET_ATTR( win, key, attrval, flag, ierr )
        if (.not. flag) then
           print *, 'Error: flag false for Win_get_attr: ', msg
           errs = errs + 1
           return
        endif
        if (attrval .ne. expected) then
           print *, 'Error: expected ', expected, ' but saw ',&
                & attrval, ':', msg
           errs = errs + 1
        endif
        return
      end subroutine fmpi2readwin

      subroutine fmpi2readtype( dtype, key, expected, msg, errs )
        use mpi_f08
        implicit none
        integer key, errs
        TYPE(MPI_Datatype) dtype
        integer (kind=MPI_ADDRESS_KIND) expected
        character *(*) msg
        logical flag
        integer ierr
        integer (kind=MPI_ADDRESS_KIND) attrval
!
        call MPI_TYPE_GET_ATTR( dtype, key, attrval, flag, ierr )
        if (.not. flag) then
           print *, 'Error: flag false for Type_get_attr: ', msg
           errs = errs + 1
           return
        endif
        if (attrval .ne. expected) then
           print *, 'Error: expected ', expected, ' but saw ',&
                & attrval, ':', msg
           errs = errs + 1
        endif
        return
      end subroutine fmpi2readtype

      subroutine f2tof2test(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr
        TYPE(MPI_Comm) fdup
        TYPE(MPI_Datatype) tdup
        integer (kind=MPI_ADDRESS_KIND) fcomm2attr, ftype2attr,&
             & fwin2attr, attrval
!
        fcomm2attr = bigaint()
        attrval    = fcomm2attr
        call MPI_COMM_SET_ATTR( MPI_COMM_SELF, fcomm2_key, fcomm2attr,&
             & ierr )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "F2 to F2",&
             & errs )
        call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
        call fmpi2read( fdup, fcomm2_key, attrval + 1, "F2 to F2 dup",&
             & errs )
        call MPI_COMM_FREE( fdup, ierr )
!
        ftype2attr = bigaint()-9
        attrval    = ftype2attr
        call MPI_TYPE_SET_ATTR( MPI_INTEGER, ftype2_key, ftype2attr,&
             & ierr )
        call fmpi2readtype( MPI_INTEGER, ftype2_key, attrval, "F2 type&
             & to F2", errs )
        call MPI_TYPE_DUP( MPI_INTEGER, tdup, ierr )
        call fmpi2readtype( tdup, ftype2_key, attrval + 1, "F2 type to&
             & F dup", errs )
        call MPI_TYPE_FREE( tdup, ierr )

        fwin2attr = bigaint()-9
        attrval    = fwin2attr
        call MPI_WIN_SET_ATTR( win, fwin2_key, fwin2attr,&
             & ierr )
        call fmpi2readwin( win, fwin2_key, attrval, "F2 win to F2",&
             & errs )
!
        fcomm2attr = -bigaint()
        attrval    = fcomm2attr
        call MPI_COMM_SET_ATTR( MPI_COMM_SELF, fcomm2_key, fcomm2attr,&
             & ierr )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "F2 to F2",&
             & errs )
        call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
        call fmpi2read( fdup, fcomm2_key, attrval + 1, "F2 to F2 dup",&
             & errs )
        call MPI_COMM_FREE( fdup, ierr )
!
        ftype2attr = -(bigaint()-9)
        attrval    = ftype2attr
        call MPI_TYPE_SET_ATTR( MPI_INTEGER, ftype2_key, ftype2attr,&
             & ierr )
        call fmpi2readtype( MPI_INTEGER, ftype2_key, attrval, "F2 type&
             & to F2", errs )
        call MPI_TYPE_DUP( MPI_INTEGER, tdup, ierr )
        call fmpi2readtype( tdup, ftype2_key, attrval + 1, "F2 type to&
             & F dup", errs )
        call MPI_TYPE_FREE( tdup, ierr )

        fwin2attr = -(bigaint()-9)
        attrval    = fwin2attr
        call MPI_WIN_SET_ATTR( win, fwin2_key, fwin2attr,&
             & ierr )
        call fmpi2readwin( win, fwin2_key, attrval, "F2 win to F2",&
             & errs )

      end subroutine f2tof2test
!
      subroutine f1toctest( errs )
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr, attrval, fcomm1attr
        TYPE(MPI_Comm) fdup
!
        fcomm1attr = bigint()
        attrval    = fcomm1attr
        call MPI_ATTR_PUT( MPI_COMM_SELF, fcomm1_key, fcomm1attr,&
             & ierr )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, &
             & "F to F (check for F to C)", errs )
        if (intSize .eq. ptrSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call cmpif1read( fdup, fcomm1_key, attrval + 1, errs, &
                & "F to F2 dup" )
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        fcomm1attr = -bigint()
        attrval    = fcomm1attr
        call MPI_ATTR_PUT( MPI_COMM_SELF, fcomm1_key, fcomm1attr,&
             & ierr )
        call fmpi1read( MPI_COMM_SELF, fcomm1_key, attrval, &
             &"F to F (check neg value for F to C)", errs )
        if (intSize .eq. ptrSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call cmpif1read( fdup, fcomm1_key, attrval + 1, errs, &
                "F to C dup" )
           call MPI_COMM_FREE( fdup, ierr )
        endif

      end subroutine f1toctest

      subroutine f2tof1test(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr
        TYPE(MPI_Comm) fdup
        TYPE(MPI_Datatype) tdup
        integer (kind=MPI_ADDRESS_KIND) fcomm2attr, ftype2attr,&
             & fwin2attr, attrval
        integer expected
!
        fcomm2attr = bigaint()
        attrval    = fcomm2attr
        call MPI_COMM_SET_ATTR( MPI_COMM_SELF, fcomm2_key, fcomm2attr,&
             & ierr )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "F2 to F2",&
             & errs )
        if (aintSize .eq. intSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           expected = attrval + 1
           call fmpi1read( fdup, fcomm2_key, expected, "F2 to F1 dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        fcomm2attr = -bigaint()
        attrval    = fcomm2attr
        call MPI_COMM_SET_ATTR( MPI_COMM_SELF, fcomm2_key, fcomm2attr,&
             & ierr )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "F2 to F2",&
             & errs )
        if (aintSize .eq. intSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           expected = attrval + 1
           call fmpi1read( fdup, fcomm2_key, expected, "F2 to F1 dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif

!
      end subroutine f2tof1test
!
      subroutine f2toctest(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr
        TYPE(MPI_Comm) fdup
        TYPE(MPI_Datatype) tdup
        integer (kind=MPI_ADDRESS_KIND) fcomm2attr, ftype2attr,&
             & fwin2attr, attrval
!
        fcomm2attr = bigaint()
        attrval    = fcomm2attr
        call MPI_COMM_SET_ATTR( MPI_COMM_SELF, fcomm2_key, fcomm2attr,&
             & ierr )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "F2 to F2",&
             & errs )
        if (aintSize .eq. ptrSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call cmpif2read( fdup, fcomm2_key, attrval + 1, errs, "F2 t&
                &o c dup")
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        ftype2attr = bigaint()-9
        attrval    = ftype2attr
        call MPI_TYPE_SET_ATTR( MPI_INTEGER, ftype2_key, ftype2attr,&
             & ierr )
        call fmpi2readtype( MPI_INTEGER, ftype2_key, attrval, "F2 type&
             & to F2", errs )
        if (aintSize .eq. ptrSize) then
           call MPI_TYPE_DUP( MPI_INTEGER, tdup, ierr )
           call cmpif2readtype( tdup, ftype2_key, attrval + 1, errs, "F2 &
                &type toF dup" )
           call MPI_TYPE_FREE( tdup, ierr )
        endif

        fwin2attr = bigaint()-9
        attrval    = fwin2attr
        call MPI_WIN_SET_ATTR( win, fwin2_key, fwin2attr, ierr )
        call cmpif2readwin( win, fwin2_key, attrval, errs, "F2 win to &
             &c" )
!
        fcomm2attr = -bigaint()
        attrval    = fcomm2attr
        call MPI_COMM_SET_ATTR( MPI_COMM_SELF, fcomm2_key, fcomm2attr,&
             & ierr )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "F2 to F2",&
             & errs )
        if (aintSize .eq. ptrSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call cmpif2read( fdup, fcomm2_key, attrval + 1, errs, "F2 t&
                &o c dup")
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        ftype2attr = -(bigaint()-9)
        attrval    = ftype2attr
        call MPI_TYPE_SET_ATTR( MPI_INTEGER, ftype2_key, ftype2attr,&
             & ierr )
        call fmpi2readtype( MPI_INTEGER, ftype2_key, attrval, "F2 type&
             & to F2", errs )
        if (aintSize .eq. ptrSize) then
           call MPI_TYPE_DUP( MPI_INTEGER, tdup, ierr )
           call cmpif2readtype( tdup, ftype2_key, attrval + 1, errs, "F2 &
                &type toF dup" )
           call MPI_TYPE_FREE( tdup, ierr )
        endif

        fwin2attr = -(bigaint()-9)
        attrval    = fwin2attr
        call MPI_WIN_SET_ATTR( win, fwin2_key, fwin2attr, ierr )
        call cmpif2readwin( win, fwin2_key, attrval, errs, "F2 win to &
             &c" )

      end subroutine f2toctest
!
      subroutine ctof2test(errs)
        use mpi_f08
        use keyvals
        implicit none
        integer errs
        integer ierr
        TYPE(MPI_Comm) fdup
        TYPE(MPI_Datatype) tdup
        integer (kind=MPI_ADDRESS_KIND) fcomm2attr, ftype2attr,&
             & fwin2attr, attrval
!
        fcomm2attr = bigaint()
        attrval    = fcomm2attr
        call csetmpi2( MPI_COMM_SELF, fcomm2_key, fcomm2attr, errs )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "c to F2",&
             & errs )
        if (aintSize .eq. ptrSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call fmpi2read( fdup, fcomm2_key, attrval + 1, "c to F2 dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        ftype2attr = bigaint()-9
        attrval    = ftype2attr
        call csetmpitype( MPI_INTEGER, ftype2_key, ftype2attr, errs )
        call fmpi2readtype( MPI_INTEGER, ftype2_key, attrval, "c type&
             & to F2", errs )
        if (aintSize .eq. ptrSize) then
           call MPI_TYPE_DUP( MPI_INTEGER, tdup, ierr )
           call fmpi2readtype( tdup, ftype2_key, attrval + 1, "c type to&
                & F2 dup", errs )
           call MPI_TYPE_FREE( tdup, ierr )
        endif

        fwin2attr = bigaint()-9
        attrval    = fwin2attr
        call csetmpiwin( win%MPI_VAL, fwin2_key, fwin2attr, errs )
        call fmpi2readwin( win, fwin2_key, attrval, "c win to F2",&
             & errs )
!
        fcomm2attr = -bigaint()
        attrval    = fcomm2attr
        call csetmpi2( MPI_COMM_SELF, fcomm2_key, fcomm2attr, errs )
        call fmpi2read( MPI_COMM_SELF, fcomm2_key, attrval, "c to F2",&
             & errs )
        if (aintSize .eq. ptrSize) then
           call MPI_COMM_DUP( MPI_COMM_SELF, fdup, ierr )
           call fmpi2read( fdup, fcomm2_key, attrval + 1, "c to F2 dup",&
                & errs )
           call MPI_COMM_FREE( fdup, ierr )
        endif
!
        ftype2attr = -(bigaint()-9)
        attrval    = ftype2attr
        call csetmpitype( MPI_INTEGER, ftype2_key, ftype2attr, errs )
        call fmpi2readtype( MPI_INTEGER, ftype2_key, attrval, "c type&
             & to F2", errs )
        if (aintSize .eq. ptrSize) then
           call MPI_TYPE_DUP( MPI_INTEGER, tdup, ierr )
           call fmpi2readtype( tdup, ftype2_key, attrval + 1, "c type to&
                & F2 dup", errs )
           call MPI_TYPE_FREE( tdup, ierr )
        endif

        fwin2attr = -(bigaint()-9)
        attrval    = fwin2attr
        call csetmpiwin( win%MPI_VAL, fwin2_key, fwin2attr, errs )
        call fmpi2readwin( win, fwin2_key, attrval, "c win to F2",&
             & errs )

      end subroutine ctof2test

! -------------------------------------------------------------------
! Return an integer value that fills all of the bytes
      pure integer function bigint()
        integer i, v, digits
        digits = range(i)
        v = 0
        do i=1,digits
           v = v * 10 + i
        enddo
        bigint = v
        return
      end function bigint
!
! Return an integer value that fill all of the bytes in an AINT
! The logical "useintsize" allows us to specify that only an int-sized
! result should be returned
      pure function bigaint()
        use mpi_f08, only : MPI_ADDRESS_KIND
        use keyvals, only : useintsize
        implicit none
        integer (kind=MPI_ADDRESS_KIND) bigaint, v
        integer i, digits
        if (useintsize) then
           digits = range(i)
        else
           digits = range(v)
        endif
        v = 0
        do i=1,digits
           v = v * 10 + i
        enddo
        bigaint = v
        return
      end function bigaint
