module LexicalSort

! This module was made available to the comp.lang.fortran newsgroup
! Author unkown

   implicit none

   public  :: sort
   private :: partition, quicksort, swap, stringComp, UpperCase

   integer, dimension (:), allocatable, private :: indexarray
   logical, private                             :: CaseSensitive

contains

!  Subroutine sort uses the quicksort algorithm.
!  On input, StringArray is a one-dimensional array of character strings
!  to be sorted in ascending lexical order.
!  On output, StringArray is the sorted array.
!  If the optional argument CaseInsensitive is present and .true.,
!  the sort is case-insensitive.  If CaseInsensitive is absent or
!  if it is .false., the sort is case-sensitive.
!  The characters of the elements of the string array are not modified,
!  so that if blanks or punctuation characters are to be ignored,
!  for instance, this needs to be done before calling sort.



   subroutine sort (StringArrayIn, iSortIndexArray, CaseInsensitive)

     character (len = *), dimension (:), intent (in out)  :: StringArrayIn
     integer, dimension (:), intent (out)                 :: iSortIndexArray
     logical, intent (in), optional                       :: CaseInsensitive

! Local variables
     integer  :: low, high, ios, k, i
     character (len = 50), dimension (:), allocatable  :: StringArray

     if (present(CaseInsensitive)) then
       CaseSensitive = .not. CaseInsensitive
     else
       CaseSensitive = .true.
     end if

     low = 1
     high = size(StringArrayIn)

     allocate(StringArray(high), stat = ios)
     StringArray = ' '
     StringArray = StringArrayIn

     allocate(indexarray(high), stat = ios)

     if (ios /= 0) then
       write(*, *) "Error allocating indexarray in LexicalSort::sort"
!      stop
     end if

     indexarray = (/ (k, k = low, high) /)

     call quicksort(StringArray, low, high)

     do i=low, high
     StringArrayIn(i) = StringArray(indexarray(i))
     iSortIndexArray(i) = indexarray(i)
     end do

     deallocate(indexarray, stat = ios)
     if (ios /= 0) then
       write(*, *) "Error deallocating indexarray in LexicalSort::sort"
!      stop   ! Fortran's Stop is not the best exit for Windows programming !
     end if

     return
   end subroutine sort


   recursive subroutine quicksort(StringArray, low, high)
     character (len = *), dimension (:), intent (in out) :: StringArray
     integer, intent (in)                                :: low, high

     integer :: pivotlocation
     if (low < high) then
       call partition(StringArray, low, high, pivotlocation)
       call quicksort(StringArray, low, pivotlocation - 1)
       call quicksort(StringArray, pivotlocation + 1, high)
     end if
     return
   end subroutine quicksort


   subroutine partition(StringArray, low, high, pivotlocation)
     character (len = *), dimension (:), intent (in out) :: StringArray
     integer, intent (in)                                :: low, high
     integer, intent (out)                               :: pivotlocation

     integer :: k, lastsmall

     call swap(indexarray(low), indexarray((low + high)/2))
     lastsmall = low
     do k = low + 1, high
       if (stringComp(StringArray(indexarray(k)), StringArray(indexarray(low)))) then
         lastsmall = lastsmall + 1
         call swap(indexarray(lastsmall), indexarray(k))
       end if
     end do
     call swap(indexarray(low), indexarray(lastsmall))
     pivotlocation = lastsmall
     return
   end subroutine partition


   subroutine swap(m, n)
     integer, intent (in out) :: m, n

     integer :: temp

     temp = m
     m = n
     n = temp
     return
   end subroutine swap


   function stringComp(p, q) result(lexicalLess)
     character (len = *), intent (in) :: p, q

     logical :: lexicalLess
     integer :: kq, k

     if (CaseSensitive) then
       lexicalLess = p < q
     else
       kq = 1
       do k = 1, max(len_trim(p), len_trim(q))
         if (UpperCase(p(k:k)) == UpperCase(q(k:k)) ) then
           cycle
         else
           kq = k
           exit
         end if
       end do
       lexicalLess = UpperCase(p(kq:kq)) < UpperCase(q(kq:kq))
     end if
     return
   end function stringComp


   function UpperCase(letter) result(L)
     character (len = *), intent (in) :: letter
     character (len = 1)              :: L

     character (len = 27), parameter :: Lower = "_abcdefghijklmnopqrstuvwxyz", &
                                        Upper = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     integer :: k

     k = index(Lower, letter)
     if (k > 0) then
       L = Upper(k:k)
     else
       L = letter
     end if
     return
   end function UpperCase

end module LexicalSort
