! transformmod.f90
! module procedure written by Tim Mitchell on 14.12.99
! last modification on 29.02.00
!   transforms temp deg C to temk deg K (only to be rarely used; see ExtractMod for vice versa)
!   transforms data into anom abs
!   transforms data into anom percent
!   detrends data
!   ### BubbleSort has moved to basicfun.f90 ###

module TransformMod

implicit none

contains

!*******************************************************************************
! detrend a region-set of time series

subroutine DeTrendTim (RegN,YearN,MissAccept,QPrint,ADYear,Data,RegAye,RegBee)

real, pointer, dimension (:,:)	:: Data
real, pointer, dimension (:)	:: RegAye, RegBee

integer, pointer, dimension (:)	:: ADYear

real, intent (in)	:: MissAccept
real, parameter		:: MissVal = -999.0
real			:: RegThresh, YearThresh
real			:: Num, Den
real			:: SumX, SumY, SumXX, SumYY, SumXY, En
real			:: ValidReg, ValidAye, ValidYear

integer, intent (in)	:: RegN, YearN, QPrint
integer			:: XReg, XYear
integer			:: AllocStat, ReadStatus

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

if (QPrint.EQ.1) print*, "  > Detrending a region-set of time series..."

allocate ( RegAye (RegN), &
	   RegBee (RegN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: DeTrendTim: Allocation failure #####"
RegAye = MissVal
RegBee = MissVal

YearThresh = YearN * (100.0-MissAccept)/100.0
RegThresh  = RegN  * (100.0-MissAccept)/100.0
ValidReg   = 0.0
ValidAye   = 0.0
ValidYear  = 0.0

do XReg = 1, RegN
  SumX  = 0.0						! initialise sums
  SumY  = 0.0
  SumXX = 0.0
  SumXY = 0.0
  SumXY = 0.0
  En    = 0.0
  
  do XYear = 1, YearN					! add together into sums
    if (Data(XReg,XYear).NE.MissVal.AND.ADYear(XYear).NE.MissVal) then
      SumX  = SumX  +  ADYear (XYear)
      SumY  = SumY  +  Data (XReg,XYear)
      SumXX = SumXX + (ADYear (XYear) ** 2)
      SumYY = SumYY + (Data (XReg,XYear) ** 2)
      SumXY = SumXY +  ADYear (XYear) * Data (XReg,XYear)
      En    = En    +  1.0
    end if
  end do
  
  if (En.GE.YearThresh) then				! calc a and b in y=ax+b
    Num = (En*SumXY)-(SumX*SumY)
    Den = (En*SumXX)-(SumX*SumX)
    if (Den.NE.0) RegAye(XReg) = Num / Den

    if (RegAye(XReg).NE.MissVal) RegBee(XReg) = (SumY-(RegAye(XReg)*SumX))/En
    
    ValidReg = ValidReg + 1.0
  end if
end do

do XReg = 1, RegN					! detrend data series
  if (RegAye(XReg).NE.MissVal) then
    ValidAye = ValidAye + 1.0
    
    do XYear = 1, YearN
      if (Data(XReg,XYear).NE.MissVal) then
      	Data(XReg,XYear) = Data(XReg,XYear) - (RegAye(XReg) * XYear)
      	ValidYear = ValidYear + 1.0
      end if
    end do
  else
    do XYear = 1, YearN
      Data(XReg,XYear) = MissVal
    end do
  end if
end do  

ValidReg  = 100.0 * (ValidReg  /  RegN)
ValidAye  = 100.0 * (ValidAye  /  RegN)
ValidYear = 100.0 * (ValidYear / (RegN*YearN))
  
write (99,"(3f12.4)"), ValidReg, ValidAye, ValidYear

if (QPrint.EQ.1) then
  print*, "  > Percent of valid regions for LSR : ", ValidReg
  print*, "  > Percent of regions with valid 'a': ", ValidAye
  print*, "  > Percent of valid data detrended  : ", ValidYear 
end if

end subroutine DeTrendTim

!*******************************************************************************
! converts single time series of data into slice means

subroutine SimpleSlice (JobYearN,SliceLen,MissAccept,DataVec)

real, pointer, dimension (:) 	:: DataVec

real, intent (in) 	:: MissAccept
integer, intent (in) 	:: JobYearN, SliceLen

real, allocatable, dimension (:) :: Sliced

real, parameter :: MissVal = -999.0

real :: DataEn, DataTot

integer :: AllocStat
integer :: XMem, XYear, SliceStartYear, SliceMidYear
integer :: TotalMiss, Threshold

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

allocate (Sliced(JobYearN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SimpleSlice: Allocation failure #####"
Sliced = MissVal
Threshold = nint (MissAccept*real(SliceLen)/100.0)

do SliceStartYear = 1, (JobYearN-SliceLen+1)
  SliceMidYear = SliceStartYear + floor(real(SliceLen)/2.0)
  TotalMiss    = 0
  DataEn       = 0.0
  DataTot      = 0.0
    
  do XYear = SliceStartYear, (SliceStartYear+SliceLen-1)
      if (DataVec(XYear).EQ.MissVal) then
      	TotalMiss = TotalMiss  + 1
      else
        DataTot   = DataTot + DataVec(XYear)
        DataEn    = DataEn  + 1.0
      end if
  end do
    
  if (TotalMiss.LE.Threshold.AND.DataEn.GE.1) then	! checks that no. of valid values in slice is good
    	Sliced (SliceMidYear) = DataTot / DataEn
  end if
end do

do XYear = 1, JobYearN
  DataVec(XYear) = Sliced(XYear)
end do

deallocate (Sliced, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SimpleSlice: Deallocation failure #####"

end subroutine SimpleSlice

!*******************************************************************************
! converts parallel time series of data into a time series of slice means

subroutine Sliceify (JobMemN,JobYearN,SliceLen,MissAccept,DataArray,DataVec)

real, pointer, dimension (:) 	:: DataVec
real, pointer, dimension (:,:) 	:: DataArray

real, intent (in) :: MissAccept

integer, intent (in) :: JobMemN, JobYearN, SliceLen

real, parameter :: MissVal = -999.0

real :: DataEn, DataTot

integer :: XMem, XYear, SliceStartYear, SliceMidYear
integer :: TotalMiss, Threshold

DataVec = MissVal

Threshold = nint (MissAccept*real(SliceLen*JobMemN)/100.0)

do SliceStartYear = 1, (JobYearN-SliceLen+1)
  SliceMidYear = SliceStartYear + floor(real(SliceLen)/2.0)
  TotalMiss    = 0
  DataEn       = 0.0
  DataTot      = 0.0
    
  do XMem = 1, JobMemN
    do XYear = SliceStartYear, (SliceStartYear+SliceLen-1)
      if (DataArray(XMem,XYear).EQ.MissVal) then
      	TotalMiss = TotalMiss  + 1
      else
        DataTot = DataTot + DataArray(XMem,XYear)
        DataEn  = DataEn  + 1.0
      end if
    end do
  end do
    
  if (TotalMiss.LE.Threshold.AND.DataEn.GE.1) then	! checks that no. of valid values in slice is ok
    	DataVec (SliceMidYear) = DataTot / DataEn
  end if
end do

end subroutine Sliceify

!*******************************************************************************
! converts single time series of data into slice stdev

subroutine SliceStDevCol (JobYearN,SliceLen,QSampPop,MissAccept,DataVec)

real, pointer, dimension (:) 	:: DataVec

real, intent (in) 	:: MissAccept
integer, intent (in) 	:: JobYearN, SliceLen, QSampPop 	! sample=1, pop=2

real, allocatable, dimension (:) :: Sliced

real, parameter :: MissVal = -999.0

real :: DataEn, DataTot, DataSqTot

integer :: AllocStat
integer :: XMem, XYear, SliceStartYear, SliceMidYear
integer :: TotalMiss, Threshold

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

allocate (Sliced(JobYearN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SliceStDevCol: Allocation failure #####"
Sliced = MissVal
Threshold = nint (MissAccept*real(SliceLen)/100.0)

do SliceStartYear = 1, (JobYearN-SliceLen+1)
  SliceMidYear = SliceStartYear + floor(real(SliceLen)/2.0)
  TotalMiss    = 0
  DataEn       = 0.0
  DataTot      = 0.0
  DataSqTot    = 0.0
    
  do XYear = SliceStartYear, (SliceStartYear+SliceLen-1)
      if (DataVec(XYear).EQ.MissVal) then
      	TotalMiss = TotalMiss  + 1
      else
        DataSqTot = DataSqTot + (DataVec(XYear) ** 2)
        DataTot   = DataTot   +  DataVec(XYear)
        DataEn    = DataEn    +  1.0
      end if
  end do
    
  if (TotalMiss.LE.Threshold.AND.DataEn.GE.2) then	! checks that no. of valid values in slice is good    	
    	Sliced (SliceMidYear) = (DataSqTot / DataEn) - ( (DataTot / DataEn) ** 2 )
    	if (Sliced(SliceMidYear).GT.0) then
    	  if (QSampPop.EQ.2) Sliced (SliceMidYear) = Sliced (SliceMidYear) * DataEn / (DataEn - 1)
    	  Sliced (SliceMidYear) = sqrt ( Sliced (SliceMidYear) )
    	else
    	  Sliced (SliceMidYear) = MissVal
    	end if
  end if
end do

do XYear = 1, JobYearN
  DataVec(XYear) = Sliced(XYear)
end do

deallocate (Sliced, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SliceStDevCol: Deallocation failure #####"

end subroutine SliceStDevCol

!*******************************************************************************
! converts ensemble of data into a time series of slice stdev

subroutine SliceStDevEns (JobMemN,JobYearN,SliceLen,QSampPop,MissAccept,DataArray,DataVec)

real, pointer, dimension (:) 	:: DataVec
real, pointer, dimension (:,:) 	:: DataArray

real, intent (in) :: MissAccept

integer, intent (in) :: JobMemN, JobYearN, SliceLen, QSampPop	! 1=sample, 2=pop

real, parameter :: MissVal = -999.0

real :: DataEn, DataTot, DataSqTot

integer :: XMem, XYear, SliceStartYear, SliceMidYear
integer :: TotalMiss, Threshold

DataVec = MissVal

Threshold = nint (MissAccept*real(SliceLen*JobMemN)/100.0)

do SliceStartYear = 1, (JobYearN-SliceLen+1)
  SliceMidYear = SliceStartYear + floor(real(SliceLen)/2.0)
  TotalMiss    = 0
  DataEn       = 0.0
  DataTot      = 0.0
  DataSqTot    = 0.0
    
  do XMem = 1, JobMemN
    do XYear = SliceStartYear, (SliceStartYear+SliceLen-1)
      if (DataArray(XMem,XYear).EQ.MissVal) then
      	TotalMiss = TotalMiss  + 1
      else
        DataSqTot = DataSqTot + (DataArray(XMem,XYear) ** 2)
        DataTot   = DataTot   +  DataArray(XMem,XYear)
        DataEn    = DataEn    +  1.0
      end if
    end do
  end do
    
  if (TotalMiss.LE.Threshold.AND.DataEn.GE.2) then
    	DataVec (SliceMidYear) = (DataSqTot / DataEn) - ( (DataTot / DataEn) ** 2)
    	if (DataVec (SliceMidYear).GT.0) then
    	  if (QSampPop.EQ.2) DataVec (SliceMidYear) = DataVec (SliceMidYear) * DataEn / (DataEn - 1)
    	  DataVec (SliceMidYear) = sqrt ( DataVec (SliceMidYear) )
    	else
    	  DataVec (SliceMidYear) = MissVal
    	end if
  end if
end do

end subroutine SliceStDevEns

!*******************************************************************************
! (only to be rarely used; see ExtractMod for vice versa)
! all work to be done in temp (degC); temk only for plotting purposes

subroutine TemptoTemk (RegN,DataVec,OutVec)

real, pointer, dimension (:) :: DataVec, OutVec		! def,alloc,init in call
integer, intent (in) :: RegN

real, parameter :: MissVal = -999.0
integer :: XReg

do XReg = 1, RegN
  if (DataVec(XReg).NE.MissVal) then
    OutVec (XReg) = DataVec (XReg) + 273.15
  else
    OutVec (XReg) = MissVal
  end if
end do

end subroutine TemptoTemk

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

subroutine AnomaliseAbs (RegN,BaseVec,DataVec,OutVec)

real, pointer, dimension (:) :: BaseVec,DataVec,OutVec	! def,alloc,init in call
integer, intent (in) :: RegN

real, parameter :: MissVal = -999.0
integer :: XReg

do XReg = 1, RegN
  if (DataVec(XReg).NE.MissVal.AND.BaseVec(XReg).NE.MissVal) then
    OutVec (XReg) = DataVec (XReg) - BaseVec (XReg)
  else
    OutVec (XReg) = MissVal
  end if
end do

end subroutine AnomaliseAbs

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

subroutine AnomalisePerCent (RegN,BaseVec,DataVec,OutVec)

real, pointer, dimension (:) :: BaseVec,DataVec,OutVec	! def,alloc,init in call
integer, intent (in) :: RegN

real, parameter :: MissVal = -999.0
integer :: XReg

do XReg = 1, RegN
  if (DataVec(XReg).NE.MissVal.AND.BaseVec(XReg).NE.MissVal) then
    OutVec (XReg) = ( (DataVec(XReg)-BaseVec(XReg)) / BaseVec(XReg)) * 100.0
  else
    OutVec (XReg) = MissVal
  end if
end do

end subroutine AnomalisePerCent

!*******************************************************************************
! QRetainMiss added to call on 25.8.00

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

real, pointer, dimension (:) 	:: TSInVec,TSLowVec,TSHighVec	! def,alloc,init in call
integer, intent (in) 		:: TimeN, THalf, QRetainMiss	! QRetainMiss:1=no,2=yes

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

real, parameter :: MissVal = -999.0

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

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

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
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 
allocate (TSPad(TimeN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: Allocation failure #####"
TSPad = 0.0

do XTime = 1, TimeN
  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
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
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
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)

end subroutine GaussSmooth

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

end module TransformMod
