|
| 1 | +!-----------------------------------------------------------------------------! |
| 2 | +! \file example/sort_generic.f90 |
| 3 | +! |
| 4 | +! Copyright (c) 2019 Oak Ridge National Laboratory, UT-Battelle, LLC. |
| 5 | +!-----------------------------------------------------------------------------! |
| 6 | + |
| 7 | +! Mock-up of a user-created type and comparison operator |
| 8 | +module sort_generic_extras |
| 9 | + implicit none |
| 10 | + public |
| 11 | + |
| 12 | + ! Declare an example Fortran derived type |
| 13 | + type :: FortranString |
| 14 | + character(len=:), allocatable :: chars |
| 15 | + end type |
| 16 | + |
| 17 | + ! Declare a 'less than' operator for that type |
| 18 | + interface operator(<) |
| 19 | + module procedure fortranstring_less |
| 20 | + end interface |
| 21 | + |
| 22 | +contains |
| 23 | + |
| 24 | +elemental function fortranstring_less(self, other) & |
| 25 | + result(fresult) |
| 26 | + type(FortranString), intent(in) :: self |
| 27 | + type(FortranString), intent(in) :: other |
| 28 | + logical :: fresult |
| 29 | + integer :: i, lchar, rchar |
| 30 | + |
| 31 | + if (.not. allocated(self%chars) .and. .not. allocated(other%chars)) then |
| 32 | + ! Both deallocated, therefore equal |
| 33 | + fresult = .false. |
| 34 | + return |
| 35 | + elseif (.not. allocated(self%chars)) then |
| 36 | + ! self deallocated, therefore less |
| 37 | + fresult = .true. |
| 38 | + return |
| 39 | + elseif (.not. allocated(other%chars)) then |
| 40 | + ! other deallocated, therefore greater |
| 41 | + fresult = .false. |
| 42 | + return |
| 43 | + endif |
| 44 | + |
| 45 | + ! If LHS is shorter, it is "less than" the RHS. |
| 46 | + if (len(self%chars) < len(other%chars)) then |
| 47 | + fresult = .true. |
| 48 | + return |
| 49 | + elseif (len(self%chars) > len(other%chars)) then |
| 50 | + fresult = .false. |
| 51 | + return |
| 52 | + endif |
| 53 | + |
| 54 | + ! If any character code is less than the RHS, it is less than. |
| 55 | + do i = 1, len(self%chars) |
| 56 | + lchar = ichar(self%chars(i:i)) |
| 57 | + rchar = ichar(other%chars(i:i)) |
| 58 | + if (lchar < rchar) then |
| 59 | + fresult = .true. |
| 60 | + return |
| 61 | + elseif (lchar > rchar) then |
| 62 | + fresult = .false. |
| 63 | + return |
| 64 | + endif |
| 65 | + end do |
| 66 | + |
| 67 | + ! Everything is equal: therefore not strictly "less than" |
| 68 | + fresult = .false. |
| 69 | +end function |
| 70 | + |
| 71 | +! C++-accessible comparison function for two pointers-to-strings |
| 72 | +function compare_strings(lcptr, rcptr) bind(C) & |
| 73 | + result(fresult) |
| 74 | + use, intrinsic :: ISO_C_BINDING |
| 75 | + type(C_PTR), intent(in), value :: lcptr |
| 76 | + type(C_PTR), intent(in), value :: rcptr |
| 77 | + logical(C_BOOL) :: fresult |
| 78 | + type(FortranString), pointer :: lptr |
| 79 | + type(FortranString), pointer :: rptr |
| 80 | + |
| 81 | + if (c_associated(lcptr) .and. c_associated(rcptr)) then |
| 82 | + ! Both associated: convert from C to Fortran pointers |
| 83 | + call c_f_pointer(cptr=lcptr, fptr=lptr) |
| 84 | + call c_f_pointer(cptr=rcptr, fptr=rptr) |
| 85 | + |
| 86 | + ! Compare the strings |
| 87 | + fresult = (lptr < rptr) |
| 88 | + elseif (.not. c_associated(lcptr)) then |
| 89 | + ! LHS is null => "less than" |
| 90 | + fresult = .true. |
| 91 | + elseif (.not. c_associated(rcptr)) then |
| 92 | + fresult = .false. |
| 93 | + endif |
| 94 | + |
| 95 | +end function |
| 96 | +end module |
| 97 | + |
| 98 | +program sort_generic_example |
| 99 | + use, intrinsic :: ISO_FORTRAN_ENV |
| 100 | + use, intrinsic :: ISO_C_BINDING |
| 101 | + use flc |
| 102 | + use flc_algorithm, only : argsort, INDEX_INT |
| 103 | + use sort_generic_extras, only : compare_strings, FortranString |
| 104 | + use example_utils, only : write_version, read_positive_int, STDOUT, STDIN |
| 105 | + implicit none |
| 106 | + type(FortranString), dimension(:), allocatable, target :: fs_array |
| 107 | + type(C_PTR), dimension(:), allocatable, target :: ptrs |
| 108 | + integer(INDEX_INT), dimension(:), allocatable, target :: ordering |
| 109 | + character(len=80) :: readstr |
| 110 | + integer :: arr_size, i, io_ierr |
| 111 | + |
| 112 | + call write_version() |
| 113 | + |
| 114 | + ! Read strings |
| 115 | + arr_size = read_positive_int("string array size") |
| 116 | + allocate(fs_array(arr_size)) |
| 117 | + do i = 1, arr_size |
| 118 | + write(STDOUT, "(a, i3)") "Enter string #", i |
| 119 | + read(STDIN, "(a)", iostat=io_ierr) readstr |
| 120 | + if (io_ierr == IOSTAT_END) then |
| 121 | + ! Leave further strings unallocated |
| 122 | + exit |
| 123 | + endif |
| 124 | + ! Allocate string |
| 125 | + allocate(fs_array(i)%chars, source=trim(readstr)) |
| 126 | + enddo |
| 127 | + |
| 128 | + ! Create C pointers to the Fortran objects |
| 129 | + ptrs = [(c_loc(fs_array(i)), i = 1, arr_size)] |
| 130 | + |
| 131 | + ! Use 'argsort' to determine the new ordering |
| 132 | + allocate(ordering(arr_size)) |
| 133 | + call argsort(ptrs, ordering, compare_strings) |
| 134 | + write(STDOUT, "(a, 20(i3))") "New order:", ordering |
| 135 | + |
| 136 | + ! Reorder the Fortran data |
| 137 | + fs_array = fs_array(ordering) |
| 138 | + |
| 139 | + ! Print the results |
| 140 | + write(STDOUT, *) "Sorted:" |
| 141 | + do i = 1, arr_size |
| 142 | + if (allocated(fs_array(i)%chars)) then |
| 143 | + write(STDOUT, "(i3, ': ', a)") i, fs_array(i)%chars |
| 144 | + else |
| 145 | + write(STDOUT, "(i3, ': ', a)") i, "<UNALLOCATED>" |
| 146 | + endif |
| 147 | + enddo |
| 148 | + |
| 149 | +end program |
| 150 | + |
| 151 | +!-----------------------------------------------------------------------------! |
| 152 | +! end of example/sort.f90 |
| 153 | +!-----------------------------------------------------------------------------! |
0 commit comments