! usesort.f90
! written by Tim Mitchell
! module contains assorted routines that require SortMod
! contains: RobustLSR, DeTrendCol
! requires: SortMod higher up in the use statements

module UseSort

implicit none

contains

!*******************************************************************************
! performs robust regression between x and y according to
! Emerson, J. D., and D. C. Hoaglin. 1983.  Resistant lines 
!  for y versus x.  pp. 129-65. In D. C. Hoaglin, F. Mosteller,
!  and J. W. Tukey, eds., Understanding Robust and               
!  Exploratory Data Analysis.  John Wiley & Sons.
! received in the form of an IDL program from Mark New on 26.9.00
! pro robust_reg,xx,yy,a,b,yfit,miss=miss
! translated into an f90 program by Tim Mitchell on 26.9.00
! last modified 27.09.00
! y=a+bx

subroutine RobustLSR (ValueN,ExeSeries,WyeSeries,WyeFit,Aye,Bee,Iter)

use SortMod

real, pointer, dimension (:) 		:: ExeSeries, WyeSeries, WyeFit, ExeOp, WyeOp, Result
real, pointer, dimension (:)		:: ExeLeft, ExeMid, ExeRight
real, pointer, dimension (:)		:: WyeLeft, WyeMid, WyeRight
real, allocatable, dimension (:) 	:: ExeSorted, WyeSorted

integer, pointer, dimension (:)		:: Ranks
integer, pointer, dimension (:)		:: OrderLeft, OrderMid, OrderRight

real, intent (out) 	:: Aye, Bee

integer, intent (in) 	:: ValueN
integer, intent (out) 	:: Iter

real, parameter :: MissVal = -999.0
real, parameter :: VeryLarge = 1000000.0

real :: ExeMedLeft, ExeMedMid, ExeMedRight, WyeMedLeft, WyeMedMid, WyeMedRight
real :: Aye0, Aye1, Bee0, Bee1, Bee2, Are0, Are1, DeeAreBee0, DeeAreBee1, DeeBee1
real :: OldAye, OldBee, ChangeAye, ChangeBee

integer :: XValue, XMedian, XIter
integer :: AllocStat, ExitAll
integer :: ValidN, MissN
integer :: XLeftMin,XLeftMax,XMidMin,XMidMax,XRightMin,XRightMax,LeftN,MidN,RightN

!****************************************
! initialise

allocate (ExeOp  (ValueN), &
          WyeOp  (ValueN), &
          WyeFit (ValueN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Allocation failure #####"

Aye    = MissVal
Bee    = MissVal
WyeFit = MissVal

OldAye    = MissVal
OldBee    = MissVal
ChangeAye = MissVal
ChangeBee = MissVal

Iter    = 0
ExitAll = 0

ValidN = 0
do XValue = 1, ValueN
  if (ExeSeries(XValue).EQ.MissVal.OR.WyeSeries(XValue).EQ.MissVal) then
    ExeOp(XValue) = MissVal
    WyeOp(XValue) = MissVal
  else
    ExeOp(XValue) = ExeSeries(XValue)
    WyeOp(XValue) = WyeSeries(XValue)
    ValidN = ValidN + 1
  end if
end do

!****************************************
! main routine

if (ValidN.LT.9) then
	ExitAll = 1
	Iter = 0 - ValidN
end if

if (ExitAll.EQ.0) then
  Iter = 1
  
  allocate (Ranks    (ValueN), &
            ExeSorted(ValueN), &
            WyeSorted(ValueN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Allocation failure #####"
  ExeSorted = MissVal
  WyeSorted = MissVal
  Ranks = MissVal
  
  call QuickSort (Reals=ExeLeft,OrderValid=Ranks,NMiss=MissN)
  
!  call RankVector (ValueN,MissN,ExeOp,Ranks)		! sort data
  
  do XValue = 1, ValidN
    ExeSorted (Ranks(XValue)) = ExeOp (XValue)
    WyeSorted (Ranks(XValue)) = WyeOp (XValue)
  end do
  
  XLeftMin  = 1							! specify thirds
  XLeftMax  = floor ( ValidN / 3.0 )
  LeftN     = XLeftMax
  
  XMidMin   = XLeftMax + 1
  XMidMax   = floor ( 2.0 * ValidN / 3.0 )
  MidN      = XMidMax - LeftN
  
  XRightMin = XMidMax + 1
  XRightMax = ValidN
  RightN    = XRightMax - MidN - LeftN
    
  allocate (ExeLeft (LeftN), &					! calc medians
            WyeLeft (LeftN), &
            ExeMid  (MidN),  &
            WyeMid  (MidN),  &
            ExeRight(RightN),&
            WyeRight(RightN),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Allocation failure #####"
  
  ExeLeft  = ExeSorted (XLeftMin :XLeftMax )
  ExeMid   = ExeSorted (XMidMin  :XMidMax  )
  ExeRight = ExeSorted (XRightMin:XRightMax)
  
  WyeLeft  = WyeSorted (XLeftMin :XLeftMax )
  WyeMid   = WyeSorted (XMidMin  :XMidMax  )
  WyeRight = WyeSorted (XRightMin:XRightMax)
  
  call QuickSort (Reals=ExeLeft,NMiss=MissN)
  ExeMedLeft  = ExeLeft(nint(real(LeftN-MissN)/2.0))
  call QuickSort (Reals=WyeLeft,NMiss=MissN)
  WyeMedLeft  = ExeLeft(nint(real(LeftN-MissN)/2.0))
  
  call QuickSort (Reals=ExeRight,NMiss=MissN)
  ExeMedRight  = ExeRight(nint(real(RightN-MissN)/2.0))
  call QuickSort (Reals=WyeRight,NMiss=MissN)
  WyeMedRight  = ExeRight(nint(real(RightN-MissN)/2.0))
  
  call QuickSort (Reals=ExeMid,NMiss=MissN)
  ExeMedMid  = ExeMid(nint(real(MidN-MissN)/2.0))
  call QuickSort (Reals=WyeMid,NMiss=MissN)
  WyeMedMid  = ExeMid(nint(real(MidN-MissN)/2.0))
    
!  ExeMedLeft  = MedianValue (LeftN,  ExeLeft)
!  WyeMedLeft  = MedianValue (LeftN,  WyeLeft)
  
!  ExeMedMid   = MedianValue (MidN,   ExeMid)
!  WyeMedMid   = MedianValue (MidN,   WyeMid)

!  ExeMedRight = MedianValue (RightN, ExeRight)  
!  WyeMedRight = MedianValue (RightN, WyeRight)
  
  deallocate (ExeMid,WyeLeft,WyeRight,WyeMid, stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Deallocation3 failure #####"
  
  if ((ExeMedRight - ExeMedLeft).NE.0) then
  	Bee0 = (WyeMedRight - WyeMedLeft) / (ExeMedRight - ExeMedLeft)
  else
  	Bee0 = VeryLarge
  end if
  
  allocate (Result(ValidN),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Allocation failure #####"

  do XValue = 1, ValidN
    Result(XValue) = (WyeSorted(XValue) - Bee0) * (ExeSorted(XValue) - ExeMedMid)
  end do
  
!  Aye0 = MedianValue (ValidN,Result)

  call QuickSort (Reals=Result,NMiss=MissN)
  Aye0  = Result(nint(real(ValidN-MissN)/2.0))

  do XValue = 1, ValidN
    Result(XValue) = WyeSorted(XValue) - (Bee0 * (ExeSorted(XValue) - ExeMedMid))
  end do
  
  ExeLeft  = Result (XLeftMin :XLeftMax )
  ExeRight = Result (XRightMin:XRightMax)
  
!  DeeAreBee0 = MedianValue (RightN,ExeRight) - MedianValue (LeftN,ExeLeft)

  call QuickSort (Reals=ExeRight,NMiss=MissN)
  DeeAreBee0 = ExeRight(nint(real(RightN-MissN)/2.0))

  call QuickSort (Reals=ExeLeft,NMiss=MissN)
  DeeAreBee0 = DeeAreBee0 - ExeLeft(nint(real(LeftN-MissN)/2.0))

  XIter = 0							! calculate b1 and drb1 iteratively
  do
    XIter = XIter + 1
    Iter = Iter + 1
    
    if ((ExeMedRight-ExeMedLeft).NE.0) then
    	DeeBee1 = DeeAreBee0 / (ExeMedRight-ExeMedLeft)
    else
    	DeeBee1 = VeryLarge
    end if

    if (XIter.EQ.1) Bee1 = Bee0 + DeeBee1
    
    do XValue = 1, ValidN
      Result(XValue) = WyeSorted(XValue) - (Bee1 * (ExeSorted(XValue) - ExeMedMid))
    end do
    
    ExeLeft  = Result (XLeftMin :XLeftMax )
    ExeRight = Result (XRightMin:XRightMax)
  
!    DeeAreBee1 = MedianValue (RightN,ExeRight) - MedianValue (LeftN,ExeLeft)

    call QuickSort (Reals=ExeRight,NMiss=MissN)
    DeeAreBee1 = ExeRight(nint(real(RightN-MissN)/2.0))

    call QuickSort (Reals=ExeLeft,NMiss=MissN)
    DeeAreBee1 = DeeAreBee1 - ExeLeft(nint(real(LeftN-MissN)/2.0))

    if (DeeAreBee1.GT.-0.001.AND.DeeAreBee1.LT.0.001) then
      do XValue = 1, ValidN
        Result(XValue) = WyeSorted(XValue) - (Bee1 * ExeSorted(XValue))
      end do
      
      call QuickSort (Reals=Result,NMiss=MissN)
      Aye  = Result(nint(real(ValidN-MissN)/2.0))

!      Aye = MedianValue (ValidN,Result)

      Bee = Bee1
      
      do XValue = 1, ValueN
        if (ExeSeries(XValue).NE.MissVal) then
          WyeFit(XValue) = Aye + (Bee * ExeSeries(XValue))
        end if
      end do
      
      ExitAll = 1    
    end if
					! interpolate between first and second estimates    
    if ((DeeAreBee1-DeeAreBee0).NE.0) then
      Bee2       = Bee1 - (DeeAreBee1 * ( (Bee1-Bee0) / (DeeAreBee1-DeeAreBee0) ) )
    else
      Bee2       = Bee1 - (DeeAreBee1 * VeryLarge)
    end if
    
    Bee0       = Bee1
    Bee1       = Bee2
    DeeAreBee0 = DeeAreBee1
    
    if (ExitAll.EQ.1.OR.XIter.EQ.10) exit
  end do

  deallocate (ExeSorted, WyeSorted, Result, Ranks, ExeLeft, ExeRight, stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Deallocation1 failure #####"
  								
end if

deallocate (ExeOp,WyeOp, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: RobustLSR: Deallocation2 failure #####"
  								
end subroutine RobustLSR

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

subroutine DeTrendCol (ExeSeries,WyeSeries)

real, pointer, dimension (:) :: ExeSeries, WyeSeries, WyeFit

real, parameter :: MissVal = -999.0

real :: Aye, Bee

integer :: Len, Iter, AllocStat, XValue

Len = size (ExeSeries)
if (Len.EQ.size(WyeSeries)) then
  call RobustLSR (Len,ExeSeries,WyeSeries,WyeFit,Aye,Bee,Iter)
  
  deallocate (WyeFit,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: DeTrendCol: Deallocation3 failure #####"
  
  if (Aye.NE.MissVal.AND.Bee.NE.MissVal) then
    do XValue = 1, Len
      if (ExeSeries(XValue).NE.MissVal.AND.WyeSeries(XValue).NE.MissVal) then
        WyeSeries (XValue) = WyeSeries (XValue) - Aye - (Bee * ExeSeries (XValue))
      else
        WyeSeries (XValue) = MissVal
      end if
    end do   
  else
    do XValue = 1, Len
        WyeSeries (XValue) = MissVal
    end do   
  end if
else
  print*, "  > DeTrendCol: Input vectors have incompatible sizes."
  WyeSeries = MissVal
end if

end subroutine DeTrendCol

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

end module UseSort
