! File: order_stat_test.f90
! Public domain 2006 James Van Buskirk
module mykinds
   implicit none
   integer, parameter :: sp = selected_real_kind(6,30)
   integer, parameter :: dp = selected_real_kind(15,300)
end module mykinds

module type1_mod
   use mykinds, only: sp
   implicit none
   private
   public type1, assignment(=), operator(<=), get_opcounts_type1

   integer :: opcounts = 0

   type type1
      real(sp) x
   end type type1

   interface assignment(=)
      module procedure type1_assign
   end interface assignment(=)

   interface operator(<=)
      module procedure type1_LE
   end interface operator(<=)

   contains
      elemental subroutine type1_assign(lhs, rhs)
         type(type1), intent(out) :: lhs
         type(type1), intent(in) :: rhs

         lhs%x = rhs%x
      end subroutine type1_assign

      function type1_LE(x, y)
         logical type1_LE
         type(type1), intent(in) :: x, y

         type1_LE = x%x <= y%x
         opcounts = opcounts+1
      end function type1_LE

      function get_opcounts_type1()
         integer get_opcounts_type1

         get_opcounts_type1 = opcounts
      end function get_opcounts_type1
end module type1_mod

module type2_mod
   implicit none
   private
   public type2, assignment(=), operator(<=), get_opcounts_type2

   integer :: opcounts = 0

   type type2
      integer x
   end type type2

   interface assignment(=)
      module procedure type2_assign
   end interface assignment(=)

   interface operator(<=)
      module procedure type2_LE
   end interface operator(<=)

   contains
      elemental subroutine type2_assign(lhs, rhs)
         type(type2), intent(out) :: lhs
         type(type2), intent(in) :: rhs

         lhs%x = rhs%x
      end subroutine type2_assign

      function type2_LE(x, y)
         logical type2_LE
         type(type2), intent(in) :: x, y

         type2_LE = x%x <= y%x
         opcounts = opcounts+1
      end function type2_LE

      function get_opcounts_type2()
         integer get_opcounts_type2

         get_opcounts_type2 = opcounts
      end function get_opcounts_type2
end module type2_mod

module type1_order_stat
   use type1_mod, T1 =>type1
   implicit none
   private
   public select
   contains
include 'order_stat.i90'
end module type1_order_stat

module type2_order_stat
   use type2_mod, T1 =>type2
   implicit none
   private
   public select
   contains
include 'order_stat.i90'
end module type2_order_stat

module generic_recombination
   use type1_order_stat, select_type1 => select
   use type2_order_stat, select_type2 => select
   implicit none
   interface select
      module procedure select_type1, select_type2
   end interface select
end module generic_recombination

program test
   use type1_mod, only: type1, get_opcounts_type1
   use type2_mod, only: type2, get_opcounts_type2
   use mykinds, only: sp
   use generic_recombination
   implicit none
   integer N
   type(type1), allocatable :: array1(:)
   type(type2), allocatable :: array2(:)
   integer i
   integer opcountsi, opcountsf
   real(sp) harvest
   real(sp) median

   write(*,'(a)',advance='no') 'Enter the size of the arrays:> '
   read(*,*) N
   if(N < 1) then
      write(*,'(a,i0,a)') "Sorry, arrays of size = ", N, &
         " are not allowed"
      stop
   end if
   allocate(array1(N), array2(N))
   call random_seed()
   do i = 1, N
      call random_number(harvest)
      array1(i)%x = harvest
      array2(i)%x = harvest*(N/10)
   end do
   opcountsi = get_opcounts_type1()
   if(mod(N,2) == 0) then
      call select(array1, N, (/ N/2, N/2+1 /))
      median = 0.5*(array1(N/2)%x+array1(N/2+1)%x)
   else
      call select(array1, N, (/ (N+1)/2 /))
      median = array1((N+1)/2)%x
   end if
   opcountsf = get_opcounts_type1()
   write(*,'(a)') 'Results for array1:'
   write(*,'(a,i0)') 'Number of comparisons = ',opcountsf-opcountsi
   if(N < 60) then
      do i = 1, N
         write(*,*) i, array1(i)%x <= median, array1(i)
      end do
   else
      write(*,'(a,i0,a,i0)') 'From ',1,' to ',N/2
      write(*,'(i0,a,f0.6)') count(array1(1:N/2)%x <= median),' are <= ',median
      write(*,'(i0,a,f0.6)') count(array1(1:N/2)%x > median),' are > ',median
      write(*,'(a,i0,a,i0)') 'From ',(N+3)/2,' to ',N
      write(*,'(i0,a,f0.6)') count(array1((N+3)/2:N)%x < median),' are < ',median
      write(*,'(i0,a,f0.6)') count(array1((N+3)/2:N)%x >= median),' are >= ',median
   end if
   opcountsi = get_opcounts_type2()
   if(mod(N,2) == 0) then
      call select(array2, N, (/ N/2, N/2+1 /))
      median = 0.5*(array2(N/2)%x+array2(N/2+1)%x)
   else
      call select(array2, N, (/ (N+1)/2 /))
      median = array2((N+1)/2)%x
   end if
   opcountsf = get_opcounts_type2()
   write(*,'(a)') 'Results for array2:'
   write(*,'(a,i0)') 'Number of comparisons = ',opcountsf-opcountsi
   if(N < 60) then
      do i = 1, N
         write(*,*) i, array2(i)%x <= median, array2(i)
      end do
   else
      write(*,'(a,i0,a,i0)') 'From ',1,' to ',N/2
      write(*,'(i0,a,f0.6)') count(array2(1:N/2)%x <= median),' are <= ',median
      write(*,'(i0,a,f0.6)') count(array2(1:N/2)%x > median),' are > ',median
      write(*,'(a,i0,a,i0)') 'From ',(N+3)/2,' to ',N
      write(*,'(i0,a,f0.6)') count(array2((N+3)/2:N)%x < median),' are < ',median
      write(*,'(i0,a,f0.6)') count(array2((N+3)/2:N)%x >= median),' are >= ',median
   end if
end program test
! End of file: order_stat_test.f90

