! regress.f90
! written by Tim Mitchell
! module contains routines to perform various regression operations
! contains: RelSig, RegressVectors, LinearLSRZeroVec, LinearLSRVec

module Regress

use FileNames
use PlainPerm

implicit none

contains

!*******************************************************************************
! calcs the relationship between two vectors (y=a+bx), r=Pearson correl coeff
!	n=no. valid data pairs, p=prob that r has been achieved by chance
! you may specify:
!	number of permutations to calc (lower=quicker), 
!	threshold of 'r' at which to calc 'p' (higher=quicker),
!	threshold of 'p' at which to discard 'p' information (higher=quicker)

subroutine RelSig (Exe,Wye,Aye,Bee,Enn,Are,Pea,SpecBeg,SpecEnd, &
		   SpecNPerm,SpecAreThresh,SpecPeaThresh)

real, dimension (:), pointer 		:: Exe,Wye,PermWye
integer, dimension (:,:), pointer 	:: Perm
integer, dimension (:), pointer 	:: Pair

real, intent(out) 		:: Aye,Bee,Enn,Are,Pea
integer, intent(in), optional	:: SpecNPerm		! default = 1000
real, intent(in), optional	:: SpecAreThresh	! default = 0.80
real, intent(in), optional	:: SpecPeaThresh	! default = 0.95
integer, intent(in), optional	:: SpecBeg,SpecEnd	! default = 1,NVal

integer, dimension(1) 	:: Seed
real, parameter 	:: MissVal = -999.0

real 	:: AreThresh,PeaThresh,Rand,PermAye,PermBee,PermEnn,PermAre
real    :: Sample,Larger,LargerThresh
integer :: AllocStat, SubLen,Beg,End
integer :: NVal,XVal,NPerm,XPerm,NPair,XPair,NSub,XSub

character (len=10) :: TimeText
character (len= 8) :: DateText

!write (99,*), "call RelSig"		! @@@@@@@@@@@@@@@@@@@
NVal = size(Exe)
if (size(Wye).NE.NVal) print*, "  > ##### RelSig: bad-len #####"
AreThresh = 0.80
if (present(SpecAreThresh)) AreThresh = SpecAreThresh
PeaThresh = 0.95
if (present(SpecPeaThresh)) PeaThresh = SpecPeaThresh
Beg = 1
if (present(SpecBeg)) Beg=SpecBeg 
End = NVal
if (present(SpecEnd)) End=SpecEnd

NPerm = 1000
if (present(SpecNPerm)) NPerm = SpecNPerm
NSub = 10 ; SubLen = nint(real(NPerm)/real(NSub)) ; NPerm=SubLen*NSub

!write (99,*), "call RegressVectors 1"		! @@@@@@@@@@@@@@@@@@@
call RegressVectors (Exe,Wye,Aye,Bee,Are,Enn,5,Beg=Beg,End=End)
!write (99,*), "done RegressVectors 1"		! @@@@@@@@@@@@@@@@@@@

Pea=MissVal
if (Are.GE.AreThresh) then		! 'r' is sufficiently high to bother with 
  NPair = Enn
  allocate (Perm(SubLen,NPair),Pair(NPair),PermWye(NVal),stat=AllocStat)	
  if (AllocStat.NE.0) print*, "  > ##### RelSig: Perm: alloc #####"
  PermWye=MissVal ; Perm=0
  
  XPair = 0
  do XVal = Beg,End			! link series of pairs to full data series
    if (Exe(XVal).NE.MissVal.AND.Wye(XVal).NE.MissVal) then
      XPair=XPair+1 ; Pair(XPair)=XVal
    end if
  end do
  
  Larger=0 ; Sample=NPerm ; LargerThresh=(1.0-PeaThresh)*Sample ; XSub=0
  do
    XSub=XSub+1 ; XPerm=0
!    write (99,*), "call FlatNoReplace"		! @@@@@@@@@@@@@@@@@@@
    call FlatNoReplace (Perm)		! plainperm.f90 - generate permutations
!    write (99,*), "done FlatNoReplace"		! @@@@@@@@@@@@@@@@@@@
    
    do
      XPerm=XPerm+1
      
      do XPair = 1, NPair		! fill permed wye vector
        PermWye(Pair(XPair)) = Wye(Pair(Perm(XPerm,XPair)))
      end do
    
!      write (99,*), "call RegressVectors 2"		! @@@@@@@@@@@@@@@@@@@
      call RegressVectors (Exe,PermWye,PermAye,PermBee,PermAre,PermEnn,5, &
      			   Beg=Beg,End=End)
!      write (99,*), "done RegressVectors 2"		! @@@@@@@@@@@@@@@@@@@
      
      if (PermEnn.LT.5) then
        Sample=Sample-1
        LargerThresh=(1.0-PeaThresh)*Sample
      else if (PermAre.GT.Are) then
        Larger=Larger+1
      end if
    
      if (XPerm.EQ.SubLen.OR.Larger.GT.LargerThresh) exit
    end do
    
    if (XSub.EQ.NSub.OR.Larger.GT.LargerThresh) exit
  end do
  
  if (Larger.LE.LargerThresh) Pea=(Sample-Larger)/Sample
!  write (99,"(2f6.2,4i5)"), Are,Pea,XSub,XPerm,nint(Sample),nint(Larger) ! @@@@@@@@@@@@@@@@@@@@@@@@@
  
  deallocate (Perm,Pair,PermWye,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### RelSig: Perm: dealloc #####"
end if
!write (99,*), "done RelSig"		! @@@@@@@@@@@@@@@@@@@

end subroutine RelSig

!*******************************************************************************
! produces a,b,r from y=a+bx, given x,y paired vectors

subroutine RegressVectors (Exe,Wye,Aye,Bee,Are,Enn,Min,RSS,RMSE,Beg,End)

real, dimension (:), pointer :: Exe,Wye
integer, intent(in) :: Min			! min pairs required for calc, .GE.2
real, intent(out) :: Aye,Bee,Are,Enn
real, intent(out), optional :: RSS	! residual sum of squares
real, intent(out), optional :: RMSE	! root-mean-squared-error (untested 28.4.03)
integer, intent(in), optional :: Beg,End	! only treat this range

real, parameter :: MissVal = -999.0
real    :: RealX,RealY
real    :: SumX,SumY,SumXX,SumYY,SumXY,SumN
real	:: Num,Den,One,Two
integer :: NVal,XVal,Val0,Val1

NVal = size(Exe) ; if (NVal.NE.size(Wye)) print*, "  > @@@@@ RegressVectors size"
Val0=1 ; Val1=NVal
if (present(Beg)) Val0=Beg
if (present(End)) Val1=End
if (Val0.GT.Val1.OR.Val0.LT.1.OR.Val1.GT.NVal) print*, "  > @@@@@ RegressVectors Beg,End"

SumX=0 ; SumY=0 ; SumXX=0 ; SumYY=0 ; SumXY=0 ; SumN=0
Aye=0 ; Bee=0 ; Are=0 ; Enn=0

do XVal = Val0,Val1
  if (Exe(XVal).NE.MissVal.AND.Wye(XVal).NE.MissVal) then
    SumX = SumX + Exe(XVal) 
    SumY = SumY + Wye(XVal)
    SumXX = SumXX + (Exe(XVal) * Exe(XVal)) 
    SumYY = SumYY + (Wye(XVal) * Wye(XVal)) 
    SumXY = SumXY + (Exe(XVal) * Wye(XVal))
    SumN  = SumN  + 1
  end if
end do

if (SumN.GE.real(Min)) then
  Den = (SumN*SumXX)-(SumX*SumX)
  if (Den.NE.0) then
    Num = (SumN*SumXY)-(SumX*SumY)
    Bee = Num / Den
    Aye = (SumY-(Bee*SumX))/SumN
    One = (SumXX/SumN)-((SumX/SumN)*(SumX/SumN))
    Two = (SumYY/SumN)-((SumY/SumN)*(SumY/SumN))
    
    if (One.GT.0.AND.Two.GT.0) then
      Den = sqrt(One) * sqrt(Two)
    else
      Den = 0.0
    end if
    
    if (Den.NE.0) then
      Num = (SumXY/SumN)-((SumX/SumN)*(SumY/SumN))
      Are = Num / Den
      Enn = SumN
    else
      Are = 1.0
      Enn = SumN
    end if
  end if
end if

if ((present(RSS).OR.present(RMSE))) then
 if (Enn.GT.0) then
  SumYY=0 ; SumN=0
  do XVal = Val0,Val1
    if (Exe(XVal).NE.MissVal.AND.Wye(XVal).NE.MissVal) then
      SumYY=SumYY+((Wye(XVal)-(Aye+(Bee*Exe(XVal))))**2)
      SumN=SumN+1
    end if
  end do
  if (present(RSS))  RSS  = SumYY
  if (present(RMSE)) RMSE = sqrt (SumYY / SumN)
 else
  if (present(RSS))  RSS  = MissVal
  if (present(RMSE)) RMSE = MissVal
 end if
end if

end subroutine RegressVectors

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

subroutine LinearLSRZeroVec (ExeVec,WyeVec,Aye)

real, dimension (:), pointer 	:: ExeVec, WyeVec
real, intent (out) 		:: Aye

real, parameter :: MissVal = -999.0

integer :: XYear, YearN

real :: SumXX, SumXY, SumEn

Aye = MissVal

YearN = size (ExeVec)
if (size(WyeVec).NE.YearN) print*, "  > ##### ERROR: LinearLSRZeroVec: vector mismatch #####"

SumXX = 0.0
SumXY = 0.0
SumEn = 0.0

do XYear = 1, YearN
    if (ExeVec(XYear).NE.MissVal.AND.WyeVec(XYear).NE.MissVal) then
      SumXX = SumXX + (ExeVec(XYear) * ExeVec(XYear))
      SumXY = SumXY + (ExeVec(XYear) * WyeVec(XYear))
      SumEn = SumEn + 1
    end if
end do

if (SumEn.GE.2.AND.SumXX.GT.0) Aye = SumXY / SumXX

end subroutine LinearLSRZeroVec

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

subroutine LinearLSRVec (Exe,Wye,Aye,Bee,Pea)

real, dimension (:), pointer 	:: Exe, Wye
real, intent (out) 		:: Bee, Aye, Pea	! y=a+bx, Pearson c/c

real, parameter :: MissVal = -999.0

integer :: XDatum, DataN

real :: SumX, SumY, SumXX, SumYY, SumXY, En, Num, Den

DataN = size (Exe)

Bee = MissVal
Aye = MissVal
Pea = MissVal

Num = 0.0
Den = 0.0

SumX  = 0.0
SumY  = 0.0
SumXX = 0.0
SumYY = 0.0
SumXY = 0.0
En    = 0.0

do XDatum = 1, DataN
    if (Exe(XDatum).NE.MissVal.AND.Wye(XDatum).NE.MissVal) then
      SumX  = SumX  + Exe(XDatum)
      SumY  = SumY  + Wye(XDatum)
      SumXX = SumXX + Exe(XDatum) * Exe(XDatum)
      SumYY = SumYY + Wye(XDatum) * Wye(XDatum)
      SumXY = SumXY + Exe(XDatum) * Wye(XDatum)
      En    = En    + 1.0
    end if
end do

if (En.GT.1) then
  Num = (En*SumXY)-(SumX*SumY)
  Den = (En*SumXX)-(SumX*SumX)
  if (Den.NE.0) Bee = Num / Den

  if (Bee.NE.MissVal) Aye = (SumY-(Bee*SumX))/En
    
  Num = (SumXY/En)-((SumX/En)*(SumY/En)) 
  Den =	sqrt((SumXX/En)-((SumX/En)*(SumX/En)))*sqrt((SumYY/En)-((SumY/En)*(SumY/En)))  
  if (Den.NE.0) Pea = Num / Den
end if

end subroutine LinearLSRVec

end module Regress
