! module PlainPerm
! simple routines for permutation testing
! contains: PartitionSample,FlatNoReplace

module PlainPerm

use FileNames

implicit none

contains

!*******************************************************************************
! A and B are two samples; under H0, they are two samples from the same pop
! this routine returns randomly partitioned A and B from the combined sample
! supply with the four vectors and start/finish

subroutine PartitionSample (SampleA,SampleB,PermA,PermB,BegA,EndA,BegB,EndB)

real, pointer, dimension (:) 	:: SampleA,SampleB 	! (NValue)
real, pointer, dimension (:,:) 	:: PermA,PermB		! (NPerm,NValue)

integer,pointer,dimension(:,:)  :: PoolPerm		! (NPerm,NValue)
integer,pointer,dimension(:)    :: Map		 	! (NPool)

integer, intent(in) :: BegA,EndA,BegB,EndB		! XValue indices

real, parameter :: MissVal = -999.0

integer :: AllocStat
integer :: XValidAye,XValidBee,XAye,XBee,XPerm,XPool
integer :: NValidAye,NValidBee,NAye,NBee,NPerm,NPool

! write (99,*), "running PartitionSample..."		! @@@@@@@@@@@@@@@@@@@@

NPerm=size(PermA,1) ; NAye=size(PermA,2) ; NBee=size(PermB,2)
if (size(SampleA).NE.NAye.OR.size(SampleB).NE.NBee) &
		print*, "  > ##### PartitionSample: Sample* #####"
if (size(PermB,1).NE.NPerm) &
		print*, "  > ##### PartitionSample: Perm* #####"
PermA=MissVal ; PermB=MissVal

NValidAye=0 ; NValidBee=0		! find valid sizes
do XAye = BegA,EndA
  if (SampleA(XAye).NE.MissVal) NValidAye=NValidAye+1
end do
do XBee = BegB,EndB
  if (SampleB(XBee).NE.MissVal) NValidBee=NValidBee+1
end do
NPool=NValidAye+NValidBee
if (NValidAye.LT.2.OR.NValidBee.LT.2.OR.NPool.LT.8) NPool=0

if (NPool.GT.0) then		! sufficient in each sample
  allocate (PoolPerm(NPerm,NPool),Map(NPool), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### PartitionSample: Alloc #####"
  XPool=0
  do XAye = BegA,EndA		! store sample indices in Pool
    if (SampleA(XAye).NE.MissVal) then
      XPool=XPool+1 ; Map(XPool)=XAye
    end if
  end do
  do XBee = BegB,EndB
    if (SampleB(XBee).NE.MissVal) then
      XPool=XPool+1 ; Map(XPool)=XBee
    end if
  end do
  
  call FlatNoReplace (PoolPerm)	! flat random version of pooled sample
  
  do XPerm = 1, NPerm		! each permutation
    do XPool = 1, NValidAye		! each valid place in sample A
      if (PoolPerm(XPerm,XPool).LE.NValidAye) then	! datum from A
        PermA(XPerm,Map(XPool)) = SampleA(Map(PoolPerm(XPerm,XPool)))
      else						! datum from B
        PermA(XPerm,Map(XPool)) = SampleB(Map(PoolPerm(XPerm,XPool)))
      end if
    end do
    do XPool = (NValidAye+1), NPool	! each valid place in sample B 
      if (PoolPerm(XPerm,XPool).LE.NValidAye) then	! datum from A
        PermB(XPerm,Map(XPool)) = SampleA(Map(PoolPerm(XPerm,XPool)))
      else						! datum from B
        PermB(XPerm,Map(XPool)) = SampleB(Map(PoolPerm(XPerm,XPool)))
      end if
    end do
  end do
    
  deallocate (PoolPerm,Map, stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### PartitionSample: Dealloc #####"
end if

! write (99,*), "done PartitionSample..."		! @@@@@@@@@@@@@@@@@@@@

end subroutine PartitionSample

!*******************************************************************************
! supply with a suitably sized array (NPerm,NVal)
! returned, filled with flat-random indices
! each XPerm has vector (1...NVal) filled with each value (1...NVal) once only
! this is suitable for selection without replacement

subroutine FlatNoReplace (Perm)

integer, pointer, dimension (:,:) 	:: Perm
logical, pointer, dimension (:) 	:: Free
integer, dimension(1) 			:: Seed

real, parameter :: MissVal = -999.0

integer :: NPerm,NVal,XPerm,XVal,XPlace,Match,NFree,XFree,AllocStat
real :: Rand
character (len=20) :: TimeText20
character (len=10) :: TimeText
character (len= 8) :: DateText

! write (99,*), "running FlatNoReplace..."	! @@@@@@@@@@@@@@@@@@@@

NPerm = size(Perm,1) ; NVal = size(Perm,2) ; Perm=MissVal

allocate (Free(NVal),stat=AllocStat)		
if (AllocStat.NE.0) print*, "  > ##### FlatNoReplace: Free: alloc #####"

call date_and_time (DateText, TimeText)
TimeText20 = adjustr(TimeText(8:10))
Seed(1) = GetIntFromText(TimeText20)		! filenames.f90
! call random_seed (put=Seed)			! compaq fortran version
call random_seed 

do XPerm = 1, NPerm		! each perm
  Free=.TRUE. ; NFree=NVal
  
  do XPlace = 1, NVal		! each place
    call random_number (Rand)
    Match = ceiling((Rand*NFree))	! random index = 1...NFree
    
    XVal=0 ; XFree=0
    do					! iterate to find this free value
      XVal=XVal+1
      if (Free(XVal)) XFree=XFree+1
      if (XFree.EQ.Match) then
        Perm(XPerm,XPlace) = XVal
        Free(XVal) = .FALSE.
        NFree=NFree-1
      end if
      if (Perm(XPerm,XPlace).GT.0) exit
    end do
  end do
end do

deallocate (Free,stat=AllocStat)		
if (AllocStat.NE.0) print*, "  > ##### FlatNoReplace: Free: dealloc #####"

! write (99,*), "done FlatNoReplace..."	! @@@@@@@@@@@@@@@@@@@@

end subroutine FlatNoReplace

!*******************************************************************************

end module PlainPerm
