MODULE sort_mod

  ! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ! Sorting algorithms
  ! ~~~~~~~~~~~~~~~~~~~
  ! Contains quicksort (qsort) and insertion sort (isort)
  ! -> Sorts an array (real only for now) by increasing values
  ! -> If wanted, output indexes of permutations if you want to apply the same sorting to other arrays
  !          ( this is quicker than having other arrays going through the whole process )
  ! 
  ! TODO : Extend interface to integers - Enable decreasing order
  ! 
  ! Author : J. Vatant d'Ollone - 2018
  ! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  IMPLICIT NONE

  INTERFACE qsort
     MODULE PROCEDURE qsort_r
     MODULE PROCEDURE qsort_outp_r
  END INTERFACE qsort

  INTERFACE isort
     MODULE PROCEDURE isort_r
     MODULE PROCEDURE isort_outp_r
  END INTERFACE isort

CONTAINS

  ! 1-pivot quicksort
  RECURSIVE SUBROUTINE qsort_r(A,n)

    IMPLICIT NONE

    ! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ! I/O
    !logical,               intent(in)    :: o !! Order T=increasing/F=decreasing
    integer,               intent(in)    :: n !! Size of array
    real,    dimension(n), intent(inout) :: A !! Array to sort

    ! Local variables
    integer :: left, right
    real    :: random
    real    :: pivot
    real    :: temp
    integer :: marker
    ! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    if (n > 1) then

       call random_number(random)
       pivot = A(int(random*real(n-1))+1) ! random pivot (not best performance, but avoids worst-case)
       left = 0
       right = n + 1

       do while (left < right)
          right = right - 1
          do while (A(right) > pivot)
             right = right - 1
          enddo
          left = left + 1
          do while (A(left) < pivot)
             left = left + 1
          enddo
          if (left < right) then
             temp     = A(left)
             A(left)  = A(right)
             A(right) = temp
          endif
       enddo

       if (left == right) then
          marker = left + 1
       else
          marker = left
       endif

       call qsort(A(:marker-1), marker-1)
       call qsort(A(marker:),   n-marker+1)

    endif

  END SUBROUTINE qsort_r


  ! 1-pivot quicksort + output array of permutations
  RECURSIVE SUBROUTINE qsort_outp_r(A,n,P)

    IMPLICIT NONE

    ! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ! I/O
    !logical,               intent(in)    :: o !! Order T=increasing/F=decreasing
    integer,               intent(in)    :: n !! Size of array
    integer, dimension(n), intent(inout) :: P !! Output array of permutations
    real,    dimension(n), intent(inout) :: A !! Array to sort

    ! Local variables
    integer :: i
    integer :: left, right
    real    :: random
    real    :: pivot
    real    :: tempA
    integer :: tempP
    integer :: marker
    ! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    if (n > 1) then

       call random_number(random)
       pivot = A(int(random*real(n-1))+1) ! random pivot (not best performance, but avoids worst-case)
       left = 0
       right = n + 1

       do while (left < right)
          right = right - 1
          do while (A(right) > pivot)
             right = right - 1
          enddo
          left = left + 1
          do while (A(left) < pivot)
             left = left + 1
          enddo
          if (left < right) then
             tempA    = A(left)
             tempP    = P(left)
             A(left)  = A(right)
             P(left)  = P(right)
             A(right) = tempA
             P(right) = tempP
          endif
       enddo

       if (left == right) then
          marker = left + 1
       else
          marker = left
       endif

       call qsort(A(:marker-1), marker-1, P(:marker-1))
       call qsort(A(marker:),   n-marker+1, P(marker:))

    endif

  END SUBROUTINE qsort_outp_r


  ! Insertion sort
  SUBROUTINE isort_r(A,n)
    IMPLICIT NONE

    INTEGER,               INTENT(in)    :: n !! Size of array
    REAL,    DIMENSION(n), INTENT(inout) :: A !! Array to sort

    INTEGER ::  i, j
    REAL    ::  x

    DO i = 2, n
       x = a(i)
       j = i - 1
       DO WHILE (j >= 1)
          IF (a(j) <= x) EXIT
          a(j + 1) = a(j)
          j = j - 1
       ENDDO
       a(j + 1) = x
    ENDDO
  END SUBROUTINE isort_r


  ! Insertion sort + output array of permutations
  SUBROUTINE isort_outp_r(A,n,P)
    IMPLICIT NONE

    INTEGER,               INTENT(in)    :: n !! Size of array
    INTEGER, DIMENSION(n), INTENT(inout) :: P !! Output array of permutations
    REAL,    DIMENSION(n), INTENT(inout) :: A !! Array to sort

    INTEGER ::  i, j, px
    REAL    ::  x

    DO i = 2, n
       x = a(i)
       px = p(i)
       j = i - 1
       DO WHILE (j >= 1)
          IF (a(j) <= x) EXIT
          a(j + 1) = a(j)
          p(j + 1) = p(j)
          j = j - 1
       ENDDO
       a(j + 1) = x
       p(j + 1) = px
    ENDDO
  END SUBROUTINE isort_outp_r


END MODULE sort_mod

