! time.f90
! module to hold standard routines for manipulating temporal things
! contains: GaussSmooth,GetMonthLengths,CommonVecPer

module Time

implicit none

contains

!*******************************************************************************
! compare year vectors to find common period

subroutine CommonVecPer (AYears,BYears,AStart,AEnd,BStart,BEnd)

integer, pointer, dimension (:) :: AYears, BYears

integer, intent (out) :: AStart,BStart,AEnd,BEnd

integer :: AYearN,BYearN,ARest,BRest
integer :: Offset,CommonLen

real, parameter :: MissVal = -999.0

AYearN = size (AYears)
BYearN = size (BYears)

Offset = BYears(1) - AYears(1) + 1
if (Offset.GE.1) then
  AStart = Offset
  BStart = 1
else
  AStart = 1
  BStart = 2 - Offset
end if

ARest = AYearN - AStart
BRest = BYearN - BStart

if (ARest.GE.0.AND.BRest.GE.0) then
  if (ARest.LT.BRest) then
    CommonLen = ARest + 1
  else
    CommonLen = BRest + 1
  end if
  
  AEnd = AStart + CommonLen - 1
  BEnd = BStart + CommonLen - 1  
else
  AStart=MissVal
  BStart=MissVal
  AEnd=MissVal
  BEnd=MissVal
end if

end subroutine CommonVecPer

!*******************************************************************************
! smooths using Gaussian filtering

subroutine GaussSmooth (TimeN,THalf,QRetainMiss,TSInVec,TSLowVec,TSHighVec,MissThresh)

real, pointer, dimension (:) 	:: TSInVec,TSLowVec,TSHighVec	! def,alloc,init in call
integer, intent (in) 		:: TimeN, THalf, QRetainMiss	! QRetainMiss:1=no,2=yes
real, intent (in), optional	:: MissThresh			! %miss=OK ; if>,output=MissVal

real, allocatable, dimension (:) 	:: Weight, TSPad, TSPadExt

real, parameter :: MissVal = -999.0

real	:: WeightFactor, WeightRoot, WeightSum
real    :: LocalTot, LocalN
real    :: MeanStart, MeanEnd
real	:: Real0, Real1
real    :: MissPercent

integer :: AllocStat
integer :: Integer0, Integer1
integer :: WeightN
integer :: XWeight, XTime, XValue
integer :: EndN
integer :: MissTot, QSetMiss

QSetMiss = 0

if (present(MissThresh)) then
 MissTot = 0
 do XTime = 1, TimeN
  if (TSInVec(XTime).EQ.MissVal) MissTot = MissTot + 1
 end do
 MissPercent = (real(MissTot) / real(TimeN)) * 100.0

 if (MissPercent.GE.MissThresh) then
  TSInVec = 999 ; QSetMiss = 1
 end if
end if

!write (99,*), "##### computing no. weights"			! ###################

WeightN = nint ((real(THalf)/2.5)+0.5)		! compute no. weights required
if (modulo(WeightN,2).EQ.0) then
	WeightN = WeightN + 2
else
	WeightN = WeightN + 1
end if
if (WeightN.LE.7) WeightN = 7

allocate (Weight(WeightN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: Allocation failure #####"
Weight = 0.0
						! compute weights
!write (99,*), "##### computing weights"				! ###################

WeightFactor = -18.0 / (real(THalf*THalf))
WeightRoot   = 1.0 / sqrt (2.0 * 3.1415927)
Weight (1)   = WeightRoot
WeightSum    = Weight (1)

do XWeight = 2, WeightN
  Weight (XWeight) = WeightRoot * exp (WeightFactor * real(XWeight) * real(XWeight))
  WeightSum = WeightSum + 2.0 * Weight (XWeight)
end do

do XWeight = 1, WeightN
  Weight (XWeight) = Weight (XWeight) / WeightSum
end do
						! pad the ts with local mean where missing 
!write (99,*), "##### padding where missing"			! ###################

allocate (TSPad(TimeN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: Allocation failure #####"
TSPad = 0.0

do XTime = 1, TimeN
!  write (99,"(i4,f8.2)"), XTime,TSInVec(XTime)			! ###################
  if (TSInVec(XTime).EQ.MissVal) then
    Real0  = max ((XTime-WeightN+1),1)
    Real1  = min ((XTime+WeightN-1),TimeN)
    
    Integer0 = nint (Real0)
    Integer1 = nint (Real1)
    
    LocalTot = 0.0
    LocalN   = 0.0

    do XValue = Integer0, Integer1
      if (TSInVec(XValue).NE.MissVal) then
        LocalTot = LocalTot + TSInVec(XValue)
        LocalN   = LocalN   + 1.0
      end if
    end do
    
    if (LocalN.GT.0.AND.LocalTot.NE.0) TSPad (XTime) = LocalTot / LocalN
  else
    TSPad (XTime) = TSInVec (XTime) 
  end if
end do
						! extend ends of ts by mean from each end
!write (99,*), "##### extending ends"			! ###################

EndN = WeightN - 1

LocalTot = 0.0
LocalN   = 0.0
do XTime = 1, EndN
        LocalTot = LocalTot + TSPad(XTime)
        LocalN   = LocalN   + 1.0
end do
MeanStart = LocalTot / LocalN

LocalTot = 0.0
LocalN   = 0.0
do XTime = (TimeN-EndN+1), TimeN
        LocalTot = LocalTot + TSPad(XTime)
        LocalN   = LocalN   + 1.0
end do
MeanEnd = LocalTot / LocalN

allocate (TSPadExt(TimeN+2*EndN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: Allocation failure #####"
TSPadExt = 0.0

do XValue = 1, EndN
  TSPadExt (XValue) = MeanStart
end do
do XValue = (EndN+1), (EndN+TimeN)
  TSPadExt (XValue) = TSPad (XValue-EndN)
end do
do XValue = (EndN+TimeN+1), (TimeN+2*EndN)
  TSPadExt (XValue) = MeanEnd
end do
						! apply the filter
!write (99,*), "##### applying filter"			! ###################
! if it fails below, it is probably because you haven't alloc all the arrays in call

do XTime = 1, TimeN
  WeightSum = Weight (1) * TSPadExt (EndN+XTime)
  
  do XWeight = 2, WeightN
    WeightSum = WeightSum + Weight(XWeight) * &
    			(TSPadExt(XTime+EndN-XWeight+1)+TSPadExt(XTime+EndN+XWeight-1))
  end do
  
  TSLowVec (XTime) = WeightSum
end do
						! compute the high-residual
!write (99,*), "##### computing high residual"		! ###################

do XTime = 1, TimeN
  TSHighVec (XTime) = TSInVec (XTime) - TSLowVec (XTime)
end do
						! reinsert MissVal unless called otherwise
if (QRetainMiss.EQ.2) then
 do XTime = 1, TimeN
  if (TSInVec(XTime).EQ.MissVal) then
   TSLowVec (XTime) = MissVal
   TSHighVec(XTime) = MissVal
  end if
 end do
end if

deallocate (Weight, TSPad, TSPadExt)

if (QSetMiss.EQ.1) then
  TSLowVec = MissVal ; TSHighVec = MissVal
end if

end subroutine GaussSmooth

!*******************************************************************************
! get month lengths appropriate to years AD

subroutine GetMonthLengths (YearAD,MonthLengths)

integer, pointer, dimension (:,:) 	:: MonthLengths
integer, pointer, dimension (:) 	:: YearAD

real    :: Rem004,Rem100,Rem400
integer :: XYear, XMonth, YearN

YearN = size (YearAD)

if (.NOT.associated(MonthLengths)) print*, &
	"  > ##### GetMonthLengths: must be alloc in call #####"

MonthLengths = 31

do XYear = 1, YearN
  MonthLengths(XYear,4 ) = 30
  MonthLengths(XYear,6 ) = 30
  MonthLengths(XYear,9 ) = 30
  MonthLengths(XYear,11) = 30
  
  Rem004 = mod (YearAD(XYear),  4)
  Rem100 = mod (YearAD(XYear),100)
  Rem400 = mod (YearAD(XYear),400)
  
  if (Rem004.EQ.0) then
    if (Rem100.EQ.0) then
      if (Rem400.EQ.0) then
        MonthLengths(XYear,2) = 29
      else
        MonthLengths(XYear,2) = 28
      end if
    else
        MonthLengths(XYear,2) = 29
    end if
  else
        MonthLengths(XYear,2) = 28
  end if
end do

end subroutine GetMonthLengths

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

end module Time
