! periodic.f90
! f90 program written by Tim Mitchell on 20.12.00
! last modified on 12.11.01
! main program with which to manipulate data from .per files
! pgf90 -Mstandard -Minfo -fast -Mscalarsse -Mvect=sse -Mflushz 
! 	-o ./../obs/periodic time.f90 filenames.f90 loadperfiles.f90 
!	saveperfiles.f90 dayfiles.f90 annfiles.f90 linfiles.f90 sortmod.f90 
!	usesort.f90 basicfun.f90 plainperm.f90 regress.f90 cetgeneral.f90 
!	./../obs/periodic.f90 2> /tyn1/tim/scratch/stderr.txt

program Periodic

use FileNames
use Time
use LoadPERFiles
use SavePERFiles
use DAYFiles
use ANNFiles
use LinFiles
use SortMod
use UseSort
use BasicFun
use PlainPerm
use Regress
use CETGeneral

implicit none

real, pointer, dimension (:,:,:)		:: Data, FileDaily
real, pointer, dimension (:,:)			:: FileMonthly, FileSeasonal, FileAll
real, pointer, dimension (:)			:: FileAnnual, OpVec, YearVec, ResultVecA, ResultVecB
real, allocatable, dimension (:,:), target	:: TargFileAll

integer, pointer, dimension (:,:)		:: MonthLengths
integer, pointer, dimension (:)			:: YearAD, FileYearAD
integer, pointer, dimension (:)			:: VariCode
integer, allocatable, dimension (:), target	:: YearTarg

character (len=80), pointer, dimension (:)		:: FileCols,FileSet
character (len=80), allocatable, dimension (:), target	:: TargFileCols
character (len= 9), pointer, dimension (:)		:: AnnLabels

character (len=80), dimension (17)		:: CalendarNames

real, parameter :: MissVal = -999.0

logical :: Verbose, Talkative

real :: MissAccept,MissThresh
real :: OpTot,OpMiss,OpEn
real :: MonMiss,MonEn,MonFrac,SeaMiss,SeaEn,SeaFrac,AnnMiss,AnnEn,AnnFrac
real :: Base,Constant,Difference,Precision,Aye,Bee, DataMin,DataMax

integer :: ReadStatus, AllocStat
integer :: MenuChoice, NextFree, NonBlank 
integer :: ChosenCol, ChosenColA, ChosenColB, ChosenPeriod, ChosenOp, ChosenSumm, ChosenPer
integer :: StartYear, EndYear, YearN, ColumnN, FileColN, SegN, SavePerN, FileN, SaveColN
integer :: XYear, XMonth, XFileYear, XMasterYear, XSeason, XPeriod, XStartYear, XEndYear, XDestineYear
integer :: XCol, XSeg, XOrigYear, XDestYear, XPer,XFile,XSaveCol
integer :: XAnnual, XFileCol
integer :: FileYear0,FileYear1,MasterYear0,MasterYear1,FileYearN,Month0,Month1
integer :: StartYearAD,EndYearAD,DestineYearAD
integer :: FileVariCode
integer :: THalf,BaseLen,DisagreeN,Iter,SegLen, TotA,TotB
integer :: QPerMon,QGradCept,QTimeCol

character (len=80) :: LoadFile,SaveFile,Blank
character (len=20) :: String20

!*******************************************************************************
! main

print*
print*, "  > ##### periodic.f90 manipulates .per files #####"
print*

call Intro

do
  print*, "  > Main menu. Make your choice. (0=list)"
  do
	read (*,*,iostat=ReadStatus), MenuChoice
	if (ReadStatus.LE.0) exit
  end do
	
  if      (MenuChoice.EQ. 1)  then
    call Conclude
    call Intro
  else if (MenuChoice.EQ. 3)  then
    call ReplicateSeg
  else if (MenuChoice.EQ. 4)  then
    call CopyPeriodCol
  else if (MenuChoice.EQ. 5)  then
    call OperateColA
  else if (MenuChoice.EQ. 6)  then
    call OperateColAB
  else if (MenuChoice.EQ. 7)  then
    call CombineColAB
  else if (MenuChoice.EQ. 8)  then
    call ReplicateYear
  else if (MenuChoice.EQ.10) then
    call LoadPerSingle
  else if (MenuChoice.EQ.12) then
    call LoadDayFile
  else if (MenuChoice.EQ.13) then
    call LoadLinFile
  else if (MenuChoice.EQ.15) then
    call LoadPerBulk
  else if (MenuChoice.EQ.20) then
    QPerMon = 1 ; call SavePerMon
  else if (MenuChoice.EQ.23) then
    call SaveLinFile
  else if (MenuChoice.EQ.24) then
    call SaveAnnCol
  else if (MenuChoice.EQ.30) then
    call CheckMaster
  else if (MenuChoice.EQ.40)  then
    call DeTrend
  else if (MenuChoice.EQ.41)  then
    call TruncRange
  else if (MenuChoice.EQ.42)  then
    call AnomalisePeriod
  else if (MenuChoice.EQ.43)  then
    call SmoothCol
  else if (MenuChoice.EQ.50)  then
    call SeasonaliseA
  else if (MenuChoice.NE.99) then
    print*, "  >  1. Reinitialise"
    print*, "  >  3. Replicate segment"
    print*, "  >  4. Copy one period into entire column"
    print*, "  >  5. Operate column  A"
    print*, "  >  6. Operate columns A.B"
    print*, "  >  7. Combine columns A.B (check disputes)" 
    print*, "  >  8. Copy one year into entire column"
    print*, "  > 10. Load .per file (single)"
    print*, "  > 12. Load .day file"
    print*, "  > 13. Load .lin file" 
    print*, "  > 15. Load .per file (batch)"
    print*, "  > 20. Save .per file"
    print*, "  > 23. Save .lin file"
    print*, "  > 24. Save .ann file"
    print*, "  > 30. Check data array"
    print*, "  > 40. Detrend (v. time or col)"
    print*, "  > 41. Truncate data range"
    print*, "  > 42. Anomalise v. period"
    print*, "  > 43. Smooth with Gaussian filter"
    print*, "  > 50. Seasonalise A --> .ann"
    print*, "  > 99. Exit"
  end if
  
  if (MenuChoice.EQ.99) exit
end do

call Conclude
print*

contains

!*******************************************************************************
! intro

subroutine Intro

open (99,file="/tyn1/tim/scratch/log-periodic.dat",status="replace",action="write")

print*, "  > Enter the number of series to allow in the master array:"
do
	read (*,*,iostat=ReadStatus), ColumnN
	if (ReadStatus.LE.0 .AND. ColumnN.GE.1) exit
end do

print*, "  > Enter the start and end years AD of the master array:"
do
	read (*,*,iostat=ReadStatus), StartYear, EndYear
	if (ReadStatus.LE.0 .AND. StartYear.LE.EndYear) exit
end do

Blank = "                                                                                 "
YearN      = EndYear - StartYear + 1
NextFree   = 1
MissAccept = 10				! % missing values that is acceptable
Talkative = .TRUE.

allocate ( Data    (ColumnN, YearN, 17), &
	   YearTarg(YearN),     	 &
	   VariCode(ColumnN),		 stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: Intro: Allocation failure #####"
Data     = MissVal
VariCode = MissVal
YearAD   => YearTarg

do XYear = 1, YearN
  YearAD (XYear) = StartYear + XYear - 1
end do

CalendarNames (1)  = "Jan" ; CalendarNames (2)  = "Feb" ; CalendarNames (3)  = "Mar"
CalendarNames (4)  = "Apr" ; CalendarNames (5)  = "May" ; CalendarNames (6)  = "Jun"
CalendarNames (7)  = "Jul" ; CalendarNames (8)  = "Aug" ; CalendarNames (9)  = "Sep"
CalendarNames (10) = "Oct" ; CalendarNames (11) = "Nov" ; CalendarNames (12) = "Dec"
CalendarNames (13) = "MAM" ; CalendarNames (14) = "JJA" ; CalendarNames (15) = "SON"
CalendarNames (16) = "DJF" ; CalendarNames (17) = "Ann"

print*

end subroutine Intro

!*******************************************************************************
! select column

subroutine SelectColumn

print*, "  > Select the column: "
do
	read (*,*,iostat=ReadStatus), ChosenCol
	if (ReadStatus.LE.0 .AND. ChosenCol.GE.1 .AND. ChosenCol.LE.ColumnN) exit
end do

end subroutine SelectColumn

!*******************************************************************************
! anomalise against period from within column

subroutine AnomalisePeriod

print*, "  > Select the column from which to calculate the base."
call SelectColumn

allocate (OpVec (17), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: AnomalisePeriod: Allocation failure #####"
OpVec = MissVal

print*, "  > Select the start,end years AD of the base period: "
do
	read (*,*,iostat=ReadStatus), StartYear, EndYear
	if (StartYear.LT.YearAD(1)) print*, "  > Too early a start. Try again."
	if (EndYear.GT.YearAD(YearN)) print*, "  > Too late a finish. Try again."
	if (StartYear.GT.EndYear) print*, "  > Try getting it the right way round. Try again."
	if (ReadStatus.LE.0.AND.StartYear.GE.YearAD(1).AND.EndYear.LE.YearAD(YearN).AND. &
		EndYear.GE.StartYear) exit
end do

XStartYear = StartYear - YearAD(1) + 1
XEndYear   = XStartYear + EndYear - StartYear
BaseLen    = EndYear - StartYear + 1
MissThresh = (MissAccept/100.0) * BaseLen

do XPeriod = 1, 17
  OpMiss = 0.0
  OpTot  = 0.0
  
  do XYear = XStartYear, XEndYear
    if (Data(ChosenCol,XYear,XPeriod).NE.MissVal) then
      OpTot  = OpTot  + Data(ChosenCol,XYear,XPeriod)
    else
      OpMiss = OpMiss + 1
    end if
  end do
  
  if (OpMiss.LE.MissThresh) then
  	OpVec(XPeriod) = OpTot / (BaseLen-OpMiss)
  end if
end do

do XPeriod = 1, 17
    do XYear = 1, YearN
    	Data(NextFree,XYear,XPeriod) = OpVec(XPeriod)
    end do
end do

VariCode(NextFree) = VariCode(ChosenCol)
call StoreMoveOn

print*, "  > Select the column to anomalise."
call SelectColumn

do XPeriod = 1, 17
  if (OpVec(XPeriod).NE.MissVal) then
    do XYear = 1, YearN
      if (Data(ChosenCol,XYear,XPeriod).NE.MissVal) &
    			Data(NextFree,XYear,XPeriod) = Data(ChosenCol,XYear,XPeriod) - OpVec(XPeriod)
    end do
  end if
end do

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

VariCode(NextFree) = VariCode(ChosenCol)
call StoreMoveOn

end subroutine AnomalisePeriod

!*******************************************************************************
! truncate data range

subroutine TruncRange

call SelectColumn

print*, "  > Select the min,max (-999=no truncation): "
do
	read (*,*,iostat=ReadStatus), DataMin,DataMax
	if (DataMin.NE.MissVal.AND.DataMax.NE.MissVal.AND.DataMin.GT.DataMax) then
	  print*, "  > Range reversed. Try again."
	  ReadStatus=1
	end if
	if (ReadStatus.LE.0) exit
end do

TotA=0 ; TotB=0
do XPeriod = 1, 17
  do XYear = 1, YearN
    if (Data(ChosenCol,XYear,XPeriod).NE.MissVal) then
      if      (DataMin.NE.MissVal.AND.Data(ChosenCol,XYear,XPeriod).LT.DataMin) then
      		Data(NextFree,XYear,XPeriod) = DataMin
      		TotA=TotA+1
      else if (DataMax.NE.MissVal.AND.Data(ChosenCol,XYear,XPeriod).GT.DataMax) then
      		Data(NextFree,XYear,XPeriod) = DataMax
      		TotB=TotB+1
      else
      		Data(NextFree,XYear,XPeriod) = Data(ChosenCol,XYear,XPeriod)
      end if
    end if
  end do
end do
print*, "  > Data truncated to min, max: ", TotA,TotB

VariCode(NextFree) = VariCode(ChosenCol)

call StoreMoveOn

end subroutine TruncRange

!*******************************************************************************
! smooth column with Gaussian filter

subroutine SmoothCol

call SelectColumn

print*, "  > Select the periodicity at which to smooth: "
do
	read (*,*,iostat=ReadStatus), THalf
	if (ReadStatus.LE.0 .AND. THalf.GE.1 .AND. THalf.LE.YearN) exit
end do

allocate (OpVec      (YearN), &
	  ResultVecA (YearN), &
	  ResultVecB (YearN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SmoothCol: Allocation failure #####"
OpVec = MissVal

do XPeriod = 1, 17
  do XYear = 1, YearN
    OpVec (XYear) = Data (ChosenCol,XYear,XPeriod)
  end do
  
  call GaussSmooth (YearN,THalf,1,OpVec,ResultVecA,ResultVecB)
  
  do XYear = 1, YearN
    Data (NextFree,XYear,XPeriod) = ResultVecA (XYear)
  end do
end do

deallocate (OpVec,ResultVecA,ResultVecB, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SmoothCol: Deallocation failure #####"

VariCode(NextFree) = VariCode(ChosenCol)

call StoreMoveOn

end subroutine SmoothCol

!*******************************************************************************
! average or summate over months into new season --> .ann

subroutine SeasonaliseA

allocate (TargFileAll  (YearN,1), & 
	  AnnLabels (1)      , &
	  MonthLengths (YearN,12), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SeasonaliseA: Allocation failure #####"
FileAll => TargFileAll
FileAll=MissVal 

call SelectColumn
call GetMonthLengths (YearAD,MonthLengths)

print*, "  > Select the first, last months in season (overlaps=OK): "
do
	read (*,*,iostat=ReadStatus), Month0,Month1
	if (ReadStatus.LE.0.AND.Month0.GE.1.AND.Month1.GE.1.AND.Month0.LE.12.AND.Month1.LE.12) exit
end do

AnnLabels (1) = trim(CalendarNames(Month0)) // "-" // trim(CalendarNames(Month1))

if (Month1.GE.Month0) then
  do XYear = 1, YearN
    OpTot  = 0.0
    OpMiss = 0.0
    OpEn   = 0.0
    
    do XMonth = Month0, Month1
      if (Data(ChosenCol,XYear,XMonth).NE.MissVal) then
        OpTot  = OpTot + Data(ChosenCol,XYear,XMonth) * MonthLengths (XYear,XMonth)
        OpEn   = OpEn  + MonthLengths (XYear,XMonth)
      else
        OpMiss = OpMiss + 1
      end if
    end do
    
    if (OpMiss.EQ.0) FileAll(XYear,1) = OpTot / OpEn
  end do
else
  do XYear = 1, (YearN-1)
    OpTot  = 0.0
    OpMiss = 0.0
    OpEn   = 0.0
    
    do XMonth = Month0, 12
      if (Data(ChosenCol,XYear,XMonth).NE.MissVal) then
        OpTot  = OpTot + Data(ChosenCol,XYear,XMonth) * MonthLengths (XYear,XMonth)
        OpEn   = OpEn  + MonthLengths (XYear,XMonth)
      else
        OpMiss = OpMiss + 1
      end if
    end do
    
    do XMonth = 1, Month1
      if (Data(ChosenCol,(XYear+1),XMonth).NE.MissVal) then
        OpTot  = OpTot + Data(ChosenCol,(XYear+1),XMonth) * MonthLengths ((XYear+1),XMonth)
        OpEn   = OpEn  + MonthLengths ((XYear+1),XMonth)
      else
        OpMiss = OpMiss + 1
      end if
    end do
    
    if (OpMiss.EQ.0) FileAll(XYear,1) = OpTot / OpEn
  end do
end if

call SaveANN (Blank, YearAD, AnnLabels, FileAll)

deallocate (FileAll,AnnLabels,MonthLengths, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SeasonaliseA: Deallocation failure #####"

print*

end subroutine SeasonaliseA

!*******************************************************************************
! replicate segments from other columns to make new column

subroutine ReplicateSeg

print*, "  > Enter the number of segments with which to make: ", NextFree
do
	read (*,*,iostat=ReadStatus), SegN
	if (ReadStatus.LE.0 .AND. SegN.GE.1) exit
end do

do XSeg = 1, SegN
  print "(a,i3)", "   > Segment ", XSeg
  
  call SelectColumn
  
  print "(a,i3)", "   > Select the start,end years AD from origin: ", ChosenCol
  do
	read (*,*,iostat=ReadStatus), StartYearAD,EndYearAD
	if (ReadStatus.LE.0 .AND. StartYearAD.GE.YearAD(1) .AND. EndYearAD.LE.YearAD(YearN) &
		.AND. StartYearAD.LE.EndYearAD) exit
  end do
  SegLen = EndYearAD - StartYearAD + 1
  
  print "(a,i3)", "   > Select the start year AD in destination:   ", NextFree
  do
	read (*,*,iostat=ReadStatus), DestineYearAD
	if (ReadStatus.LE.0 .AND. DestineYearAD.GE.YearAD(1) .AND. &
		(DestineYearAD+SegLen-1).LE.YearAD(YearN)) exit
  end do
  print "(a,i4)", "   > End year AD in destination: ", (DestineYearAD+SegLen-1)
  
  XStartYear   = StartYearAD   - YearAD(1) + 1 
  XDestineYear = DestineYearAD - YearAD(1) + 1
  
  do XYear = 1, SegLen
    XOrigYear = XStartYear   + XYear - 1
    XDestYear = XDestineYear + XYear - 1
    
    do XPeriod = 1, 17
      Data (NextFree,XDestYear,XPeriod) = Data (ChosenCol,XOrigYear,XPeriod)
    end do
  end do 
end do

call StoreMoveOn

end subroutine ReplicateSeg

!*******************************************************************************
! replicate single year from old column to make new column

subroutine ReplicateYear

call SelectColumn
  
print*, "  > Enter the year to replicate: "
do
	read (*,*,iostat=ReadStatus), StartYearAD
	XStartYear = StartYearAD - YearAD(1) + 1
	if (ReadStatus.LE.0 .AND. XStartYear.GE.1 .AND. XStartYear.LE.YearN) exit
end do

do XYear = 1, YearN
    do XPeriod = 1, 17
      Data (NextFree,XYear,XPeriod) = Data (ChosenCol,XStartYear,XPeriod)
    end do
end do

call StoreMoveOn

end subroutine ReplicateYear

!*******************************************************************************
! copy a single period (e.g. Jan) into an entire column

subroutine CopyPeriodCol

call SelectColumn

print*, "  > Select the period (1-12=months,13-16=MAM-DJF,17=annual): "
do
	read (*,*,iostat=ReadStatus), ChosenPeriod
	if (ReadStatus.LE.0 .AND. ChosenPeriod.GE.1 .AND. ChosenPeriod.LE.17) exit
end do

do XYear = 1, YearN
  do XPeriod = 1, 17
    Data (NextFree,XYear,XPeriod) = Data (ChosenCol,XYear,ChosenPeriod)
  end do
end do

VariCode(NextFree) = VariCode(ChosenCol)

call StoreMoveOn

end subroutine CopyPeriodCol

!*******************************************************************************
! operate on column A

subroutine OperateColA

print*, "  > Select the operation (A/k=1, A*k=2, A-k=3, A+k=4, sqrt=5, **k=6, abs=7): "
do
	read (*,*,iostat=ReadStatus), ChosenOp
	if (ReadStatus.LE.0 .AND. ChosenOp.GE.1 .AND. ChosenOp.LE.7) exit
end do

call SelectColumn

if (ChosenOp.NE.5.AND.ChosenOp.NE.7) then
  print*, "  > Select the constant: "
  do
	read (*,*,iostat=ReadStatus), Constant
	if (ReadStatus.LE.0) exit
  end do
end if

do XPeriod = 1, 17
  do XYear = 1, YearN
    Data(NextFree,XYear,XPeriod) = OpAwithB (Data(ChosenCol,XYear,XPeriod),Constant,ChosenOp)
  end do
end do

call StoreMoveOn

end subroutine OperateColA

!*******************************************************************************
! operate on the combination of columns A and B

subroutine OperateColAB

print*, "  > Select the operation (A/B=1, A*B=2, A-B=3, A+B=4): "
do
	read (*,*,iostat=ReadStatus), ChosenOp
	if (ReadStatus.LE.0 .AND. ChosenOp.GE.1 .AND. ChosenOp.LE.4) exit
end do

print*, "  > Select columns A and B in turn."
call SelectColumn
ChosenColA = ChosenCol
call SelectColumn
ChosenColB = ChosenCol

do XPeriod = 1, 17
  do XYear = 1, YearN
    Data(NextFree,XYear,XPeriod) = OpAwithB (Data(ChosenColA,XYear,XPeriod), &
    		Data(ChosenColB,XYear,XPeriod),ChosenOp)
  end do
end do

call StoreMoveOn

end subroutine OperateColAB

!*******************************************************************************
! combines columns A and B, checking that they don't disagree

subroutine CombineColAB

DisagreeN = 0

print*, "  > Select columns A and B in turn."
call SelectColumn
ChosenColA = ChosenCol
call SelectColumn
ChosenColB = ChosenCol

print*, "  > Enter allowable difference between columns (e.g. 0.1): "
do
	read (*,*,iostat=ReadStatus), Precision
	if (ReadStatus.LE.0) exit
end do  

do XYear = 1, YearN
  do XPeriod = 1, 17
    if (Data(ChosenColA,XYear,XPeriod).NE.MissVal) then
      if (Data(ChosenColB,XYear,XPeriod).NE.MissVal) then
        Difference = Data(ChosenColA,XYear,XPeriod) - Data(ChosenColB,XYear,XPeriod)
        
        if (abs(Difference).GT.Precision) then
          DisagreeN = DisagreeN + 1
          if (DisagreeN.EQ.1) write (99,"(a36)"), "  Year   Per    Column A    Column B"          
          write (99,"(2i6,3f12.4)"), & 
            YearAD(XYear), XPeriod, Data(ChosenColA,XYear,XPeriod), Data(ChosenColB,XYear,XPeriod), Difference
        else
          Data(NextFree,XYear,XPeriod) = Data(ChosenColA,XYear,XPeriod)
        end if
      else
        Data(NextFree,XYear,XPeriod) = Data(ChosenColA,XYear,XPeriod)
      end if
    else
      Data(NextFree,XYear,XPeriod) = Data(ChosenColB,XYear,XPeriod)
    end if
  end do
end do

if (DisagreeN.GE.1) then
  print*, "  > Columns are not in complete agreement. See log file for details."
  print*
  
  do XYear = 1, YearN
    do XPeriod = 1, 17
      Data(NextFree,XYear,XPeriod) = MissVal
    end do
  end do
else
  call StoreMoveOn
end if

end subroutine CombineColAB

!*******************************************************************************
! detrend column

subroutine DeTrend

print*, "  > Enter the column to detrend."
call SelectColumn
ChosenColA = ChosenCol

print*, "  > Detrend against time (=1) or another column (=2) ? "
do
	read (*,*,iostat=ReadStatus), QTimeCol
	if (ReadStatus.LE.0 .AND. QTimeCol.GE.1 .AND. QTimeCol.LE.2) exit
end do

if (QTimeCol.EQ.2) then
  call SelectColumn
  ChosenColB = ChosenCol
end if

print*, "  > Remove gradient only (=1) or intercept also (=2): "
do
	read (*,*,iostat=ReadStatus), QGradCept
	if (ReadStatus.LE.0 .AND. QGradCept.GE.1 .AND. QGradCept.LE.2) exit
end do

allocate (OpVec   (YearN), &
	  YearVec (YearN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: DeTrend: Allocation failure #####"
OpVec = MissVal ; YearVec = MissVal

do XPeriod = 1, 17
  do XYear = 1, YearN
    OpVec (XYear) = Data(ChosenColA,XYear,XPeriod)
    if (QTimeCol.EQ.1) YearVec (XYear) = real (YearAD(XYear))
    if (QTimeCol.EQ.2) YearVec (XYear) = Data(ChosenColB,XYear,XPeriod)
  end do
  
  call RobustLSR (YearN,YearVec,OpVec,ResultVecA,Aye,Bee,Iter)
  
  write (99,"(i4,2f12.4,i12)"), XPeriod,Aye,Bee,Iter		! records LSR info to file
    
  deallocate (ResultVecA, stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: DeTrend: Deallocation failure #####"
  
  if (Aye.NE.MissVal.AND.Bee.NE.MissVal) then
   if (QGradCept.EQ.1) then
    do XYear = 1, YearN
      if (OpVec(XYear).NE.MissVal) Data(NextFree,XYear,XPeriod) = OpVec(XYear) - (Bee * YearVec(XYear)) 
    end do
   else
    do XYear = 1, YearN
      if (OpVec(XYear).NE.MissVal) Data(NextFree,XYear,XPeriod) = OpVec(XYear) - (Aye+(Bee * YearVec(XYear))) 
    end do
   end if
  end if
end do

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

call StoreMoveOn

end subroutine DeTrend

!*******************************************************************************
! load .per file (single)

subroutine LoadPerSingle

LoadFile=Blank ; Verbose=.TRUE. ; call LoadPerFile

end subroutine LoadPerSingle

!*******************************************************************************
! load .per file (bulk)

subroutine LoadPerBulk

print*, "  > Select the .per files using a filter."
call GetBatch (Blank,FileSet)
FileN = size (FileSet, 1) ; Verbose=.FALSE. ; Talkative=.FALSE.

do XFile=1,FileN
  LoadFile=FileSet(XFile)
  call LoadPerFile
end do

deallocate (FileSet, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadPerBulk: Deallocation failure #####"
Talkative=.TRUE.

end subroutine LoadPerBulk

!*******************************************************************************
! load .per file

subroutine LoadPerFile

call PreLoadPer (LoadFile,FileYearN)

allocate (FileYearAD(FileYearN),FileMonthly(FileYearN,12), &
	  FileSeasonal(FileYearN,4),FileAnnual(FileYearN), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadPerFile: alloc #####"

call LoadPER (LoadFile,FileVariCode,FileYearAD, &
		FileMonthly,FileSeasonal,FileAnnual,Silent=1)
call CommonVecPer (FileYearAD,YearAD,FileYear0,FileYear1,MasterYear0,MasterYear1)

if (FileYear0.EQ.MissVal) then
  if (Verbose) print*, "  > The loaded file has no period in common with the master array."
else
  if (Verbose) print*, "  > Common period loaded: ", FileYearAD(FileYear0), FileYearAD(FileYear1)

  do XFileYear = FileYear0, FileYear1
    XMasterYear = MasterYear0 + XFileYear - FileYear0
  
    do XMonth = 1, 12
      Data(NextFree,XMasterYear,XMonth) = FileMonthly(XFileYear,XMonth)
    end do
  
    do XSeason = 1, 4
      Data(NextFree,XMasterYear,(XSeason+12)) = FileSeasonal(XFileYear,XSeason)
    end do
  
    Data(NextFree,XMasterYear,17) = FileAnnual(XFileYear)
  end do
end if

deallocate (FileYearAD,FileMonthly,FileSeasonal,FileAnnual, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadPerFile: Deallocation failure #####"

VariCode(NextFree) = FileVariCode

call StoreMoveOn

end subroutine LoadPerFile

!*******************************************************************************
! load .day file
! this loads a .day file and summaries it by period
! for more complicated manipulations use daily.f90

subroutine LoadDayFile

call LoadDAY (Blank,FileVariCode,FileYearAD,FileDaily)
FileYearN = size(FileDaily,1)

print*, "  > Select the summarisation (-1=min,0=mean,1=max,2=sum): "
do
	read (*,*,iostat=ReadStatus), ChosenSumm
	if (ReadStatus.LE.0 .AND. ChosenSumm.GE.-1 .AND. ChosenSumm.LE.2) exit
end do

call CommonVecPer (FileYearAD,YearAD,FileYear0,FileYear1,MasterYear0,MasterYear1)

allocate (FileMonthly  (FileYearN,12), &
	  FileSeasonal (FileYearN,4),  &
	  FileAnnual   (FileYearN),    stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadDayFile: Allocation failure #####"
FileMonthly  = MissVal
FileSeasonal = MissVal
FileAnnual   = MissVal

if (FileYear0.EQ.MissVal) then
  print*, "  > The loaded file has no period in common with the master array."
else
  print*, "  > Common period loaded: ", FileYearAD(FileYear0), FileYearAD(FileYear1)
  
  if      (ChosenSumm.EQ.-1) then
    call FillDailyMin   (MissAccept,FileYearAD,FileDaily,FileMonthly)
    call FillSeaAnnMin  (FileYearAD,FileMonthly,FileSeasonal,FileAnnual)  
  else if (ChosenSumm.EQ. 0) then
    call FillDailyMean  (MissAccept,FileYearAD,FileDaily,FileMonthly)
    call FillSeaAnnMean (FileYearAD,FileMonthly,FileSeasonal,FileAnnual)  
  else if (ChosenSumm.EQ. 1) then
    call FillDailyMax   (MissAccept,FileYearAD,FileDaily,FileMonthly)
    call FillSeaAnnMax  (FileYearAD,FileMonthly,FileSeasonal,FileAnnual)  
  else if (ChosenSumm.EQ. 2) then
    call FillDailySum   (MissAccept,FileYearAD,FileDaily,FileMonthly)
    call FillSeaAnnSum  (FileYearAD,FileMonthly,FileSeasonal,FileAnnual)  
  end if

  do XFileYear = FileYear0, FileYear1
    XMasterYear = MasterYear0 + XFileYear - FileYear0
  
    do XMonth = 1, 12
      Data(NextFree,XMasterYear,XMonth) = FileMonthly(XFileYear,XMonth)
    end do
  
    do XSeason = 1, 4
      Data(NextFree,XMasterYear,(XSeason+12)) = FileSeasonal(XFileYear,XSeason)
    end do
  
    Data(NextFree,XMasterYear,17) = FileAnnual(XFileYear)
  end do
end if

deallocate (FileYearAD,FileDaily,FileMonthly,FileSeasonal,FileAnnual, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadPerFile: Deallocation failure #####"

VariCode(NextFree) = FileVariCode

call StoreMoveOn

end subroutine LoadDayFile

!*******************************************************************************
! load .lin file

subroutine LoadLinFile

call LoadLinAuto  (FileYearAD, FileCols, FileAll)

call CommonVecPer (FileYearAD,YearAD,FileYear0,FileYear1,MasterYear0,MasterYear1)

if (FileYear0.EQ.MissVal) then
  print*, "  > The loaded file has no period in common with the master array."
else
  print*, "  > Common period: ", FileYearAD(FileYear0), FileYearAD(FileYear1)

  FileColN = size (FileCols)

  print*, "  > COL NAME"
  do XCol = 1, FileColN
    print "(i8,a1,a)", XCol, " ", adjustl(trim(FileCols(XCol)))
  end do
  
  do
    if (FileColN.NE.1) then
      print*, "  > Enter the file column to fill next master column (-1=end): "
      do
	read (*,*,iostat=ReadStatus), XFileCol
	if (ReadStatus.LE.0 .AND. XFileCol.GE.-1 .AND. XFileCol.LE.FileColN) exit
      end do
    else
      XFileCol = 1
    end if
    
    if (XFileCol.GE.1 .AND. XFileCol.LE.FileColN) then
      do XFileYear = FileYear0, FileYear1
        XMasterYear = MasterYear0 + XFileYear - FileYear0
  
        do XCol = 1, 17
          Data(NextFree,XMasterYear,XCol) = FileAll(XFileCol,XFileYear)
        end do
      end do
      
      call StoreMoveOn
    end if
    
    if (XFileCol.EQ.-1.OR.FileColN.EQ.1) exit
  end do    
end if

deallocate (FileYearAD,FileCols,FileAll, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadLinFile: Deallocation failure #####"

end subroutine LoadLinFile

!*******************************************************************************
! save .per file

subroutine SavePerMon

call SelectColumn

allocate (FileMonthly  (YearN,12), &
	  FileSeasonal (YearN,4),  &
	  FileAnnual   (YearN),    stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SavePerMon: Allocation failure #####"
FileMonthly  = MissVal
FileSeasonal = MissVal
FileAnnual   = MissVal

do XYear = 1, YearN
  do XMonth = 1, 12
    FileMonthly(XYear,XMonth) = Data(ChosenCol,XYear,XMonth)
  end do
  
  do XSeason = 1, 4
    FileSeasonal(XYear,XSeason) = Data(ChosenCol,XYear,(XSeason+12))
  end do
  
  FileAnnual(XYear) = Data(ChosenCol,XYear,17)
end do

if (QPerMon.EQ.1) call SavePER    (Blank,YearAD,VariCode(ChosenCol),Monthly=FileMonthly,&
		Seasonal=FileSeasonal,Annual=FileAnnual)

deallocate (FileMonthly,FileSeasonal,FileAnnual, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SavePerMon: Deallocation failure #####"

print*

end subroutine SavePerMon

!*******************************************************************************
! save .lin file

subroutine SaveLinFile

call SelectColumn

allocate (FileAll   (17,YearN), & 
	  FileCols  (17),       stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SaveLinFile: Allocation failure #####"
FileAll  = MissVal
FileCols = ""

do XYear = 1, YearN
  do XMonth = 1, 12
    FileAll(XMonth,XYear) = Data(ChosenCol,XYear,XMonth)
  end do
  
  do XSeason = 1, 4
    FileAll((XSeason+12),XYear) = Data(ChosenCol,XYear,(XSeason+12))
  end do
  
  FileAll(17,XYear) = Data(ChosenCol,XYear,17)
end do

call SaveLin (Blank, Blank, FileCols, YearAD, FileAll)

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

print*

end subroutine SaveLinFile

!*******************************************************************************
! save .ann file from various columns

subroutine SaveAnnCol

print*, "  > Enter the number of cols in the .ann file: "
do
	read (*,*,iostat=ReadStatus), SaveColN
	if (ReadStatus.LE.0 .AND. SaveColN.GE.1) exit
end do

allocate (FileAll   (YearN,SaveColN), & 
	  AnnLabels  (SaveColN),       stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SaveAnnCol: Alloc failure #####"
FileAll=MissVal ; AnnLabels=""

do XSaveCol=1,SaveColN
  print "(a,i4)", "   > Enter the col, per (1-17) to save to .ann col:", XSaveCol
  do
	read (*,*,iostat=ReadStatus), ChosenCol,ChosenPer
	if (ReadStatus.LE.0 .AND. ChosenPer.GE.1 .AND. ChosenPer.LE.17 .AND. &
		ChosenCol.GE.1.AND.ChosenCol.LE.ColumnN) exit
  end do

  do XYear = 1, YearN
        FileAll(XYear,XSaveCol) = Data(ChosenCol,XYear,ChosenPer)
  end do
  
  String20 = GetTextFromInt (ChosenCol)
  AnnLabels(XSaveCol) = trim(String20) // ":" // trim(CalendarNames(ChosenPer))
  AnnLabels(XSaveCol) = adjustr(AnnLabels(XSaveCol))
end do

SaveFile=Blank
call SaveANN (SaveFile, YearAD, AnnLabels, FileAll)

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

end subroutine SaveAnnCol

!*******************************************************************************
! store data in column and move on

subroutine StoreMoveOn

call CheckBlank

if (NonBlank.EQ.0) then
  if (Talkative) print*, "  > Operation has no valid results. No results stored."
  VariCode(NextFree) = MissVal
else
  if (Talkative) print*, "  > Codes for column, variable: ", NextFree, VariCode(NextFree)
  
  NextFree = NextFree + 1
  if (NextFree.GT.ColumnN) then
    NextFree = NextFree - 1
    if (Talkative) print*, "  > All columns are being used. Next free set to: ", NextFree
  end if
end if

if (Talkative) print*

end subroutine StoreMoveOn

!*******************************************************************************
! check whether column is (=0) or is not (=1) blank

subroutine CheckBlank

NonBlank = 0

XYear    = 0
do
  XYear = XYear + 1
  
  XPeriod = 0
  do
    XPeriod = XPeriod + 1
    
    if (Data(NextFree,XYear,XPeriod).NE.MissVal) NonBlank = 1
    if (NonBlank.EQ.1) exit
    if (XPeriod.EQ.17) exit
  end do
  
  if (NonBlank.EQ.1) exit
  if (XYear.EQ.YearN) exit
end do

end subroutine CheckBlank

!*******************************************************************************
! check master array

subroutine CheckMaster

print*, "  > Column  Months Seasons Annuals (missing%)"

do XCol = 1, ColumnN
 MonMiss = 0.0
 MonEn   = 0.0
 SeaMiss = 0.0
 SeaEn   = 0.0
 AnnMiss = 0.0
 AnnEn   = 0.0

 do XYear = 1, YearN
  do XMonth  = 1, 12
      if (Data (XCol,XYear,XMonth)  .NE. MissVal) then
        MonEn   = MonEn   + 1.0
      else
        MonMiss = MonMiss + 1.0
      end if
  end do

  do XSeason = 13,16
      if (Data (XCol,XYear,XSeason) .NE. MissVal) then
        SeaEn   = SeaEn   + 1.0
      else
        SeaMiss = SeaMiss + 1.0
      end if
  end do

  do XAnnual = 17,17
      if (Data (XCol,XYear,XAnnual) .NE. MissVal) then
        AnnEn   = AnnEn   + 1.0
      else
        AnnMiss = AnnMiss + 1.0
      end if
  end do
 end do

 MonFrac = 100.0 * MonMiss / (MonMiss + MonEn)
 SeaFrac = 100.0 * SeaMiss / (SeaMiss + SeaEn)
 AnnFrac = 100.0 * AnnMiss / (AnnMiss + AnnEn)

 print "(i11,3f8.2)", XCol, MonFrac, SeaFrac, AnnFrac
end do

print*

end subroutine CheckMaster

!*******************************************************************************
! conclude

subroutine Conclude

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

close (99)

end subroutine Conclude

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

end program Periodic
