program t_test
  !
  ! t-test for difference between time mean values, using monthly mean data
  !
  ! The test is conducted separately for UTC hours 'hour1' ... 'hour2'
  ! with interval 'step', as well as for the mean over all the selected
  ! UTC hours.
  !
  ! Code somehow documented: Jouni Räisänen 4.7.2023
  !  
  !----------------------------------------------------------------------
  ! Input files (text format)
  !----------------------------------------------------------------------
  ! files = monthly mean time series for the model simulation
  ! fileo = ------------------------ for observations  
  !
  ! Each data line in the input files includes:
  !
  ! 1) station code
  ! 2) longitude
  ! 3) latitude
  ! 4) year
  ! 5) month
  ! 6) variable
  ! 7) data value
  !  
  !---------------------------------------------------------------------- 
  ! Output files
  !----------------------------------------------------------------------
  !
  ! outfile = results from the t-test in odb compatible text format. Each
  !           line of the file gives the following  for one UTC hour:     
  !
  ! 1) station codde
  ! 2) longitude
  ! 3) latitude
  ! 4) UTC hour
  ! 5) code of variable
  ! 6) number of degrees of freedom in t-test (assuming zero autocorrelation)
  ! 7) t-value
  ! 8) p-value of t-value (based on a simple look-up-table approach)
  !
  ! outfile_grads = a second output file in GrADS binary format
  !                 (only if lgrads=.true.)
  !
  !--------------------------------------------------------------------
  ! Namelist parameters:
  !--------------------------------------------------------------------
  !
  ! files = monthly mean time series for the model simulation
  ! fileo = ------------------------ for observations
  ! years1,months1,years2,months2 = period used from files (if available)
  ! yearo1,montho1,yearo2,montho2 = period used from fileo (if available)
  !
  ! month1,month2 = range of months used in the t-test. 
  !        example: month1=12, month2=2 -> the test is based on
  !                 interannual variability of December-February mean values
  !
  ! hour1 = first UTC hour tested
  ! hour2 = last UTC hour test
  ! hour_step = step between tested UTC hours
  !
  ! min_fraction_valid_data = fraction of available monthly means that
  !                           is required for calculating an annual value.
  !                           (default = 0.5)  
  !  
  ! miss = missing value code (default = -9.99e6).
  !        Note that all data with an absolute value equal to or large than
  !        abs(miss) will be treated as missing.
  !
  ! outfile = output file (in ODB compatible text format)
  !
  ! lgrads = if .true., output as a GrADS binary is also written.
  !   
  ! outfile_grads = output file (as a GrADS binary), if LGRADS=.true.
  !
  ! l_code_in_char: if .true., the station code is assumed to be 
  !                 a 3-character string (as for GRUAN soundings).
  !                 Otherwise, station code as integer is assumed.
  !
  !---------------------------------------------------------------
  ! Definition of the 'annual' values used in the test:
  !---------------------------------------------------------------
  !
  ! 1) If both data sets include at least 12 months of data,
  !    then annual means include all months within the range month1-month2
  !
  ! 2) If the simulation includes less than 12 months of data
  !    then annual means exclude the months not available for the simulation
  ! 
  !    For each two time series (s and o), the period used in the
  !    test starts from the first calendar month of the simulation (months1)
  !
  ! 3) If the observed time series gives less than 2 annual means,
  !    the test cannot be conducted and the program terminates.
  ! 
  ! NB: it is assumed that the periods defined by 
  !     'year1s,month1s,year2s,month2s' and
  !     'year1o,month1o,year2o,month2o'
  !     are available in the data sets
  !
  !     If monthly means of individual months are missing, the
  !     annual mean for that year is calculated without these month(s)
  !     (and may thus be biased). However, if the fraction of available
  !     monthly means is less than min_fraction_valid_data, the
  !     annual mean is set as missing.
  !
  !     If an annual mean is missing, it is excluded from
  !     the test (-> number of years -> number of degrees of freedom is also reduced)
  !
  !     If the total number of degrees of freedom is less than 1,
  !     the test cannot be conducted.
  !
  !--------------------------------------------------------------------------------
  
  implicit none

!  ---------------- Namelist parameters ----
  character*160 :: files,fileo,outfile,outfile_grads
  integer :: year1s,month1s,year2s,month2s
  integer :: year1o,month1o,year2o,month2o
  integer :: month1,month2
  integer :: hour1,hour2,hour_step
  logical :: lgrads
  logical :: l_code_in_char
  real :: min_fraction_valid_data 
  real :: miss  
!
!---- Arrays for monthly and annual mean values ----
  integer,parameter :: nhour=24,nmonth=12,nmax_year=100
  real :: sim(nhour+1,nmonth,nmax_year)
  real :: obs(nhour+1,nmonth,nmax_year)
  real :: sim_ann(nmax_year,nhour+1)
  real :: obs_ann(nmax_year,nhour+1)
!
!---- t test statistics (for each UTC hour ---------
!
  
  real :: mean_sim(nhour+1),std_sim(nhour+1)
  real :: mean_obs(nhour+1),std_obs(nhour+1)
  integer :: n_sim(nhour+1),n_obs(nhour+1),dof(nhour+1)  
  real :: t(nhour+1)
  real :: p_value(nhour+1)
!  
!--- Miscellaneous
!
  integer :: i,j,k
  integer :: station_int      ! station code as an integer (if l_code_in_char=.false.)
  character*3 :: station_char ! station codes as 3-char string (if l_code_in_char=.true.)
  real :: longitude,latitude
  integer :: year,month,hour,variable ! values read from input data lines
  real :: f1  ! individual data value read from input data line
  integer :: nhour_anal ! number of UTC hours for which the test is conducted
  logical :: valid ! This is used for deciding whether the "all-UTC" monthly mean can be calculated
  logical :: month_used(nmonth) ! .true. for calendar months used in the t-test
  logical :: month_sim(nmonth) ! .true. for calendar months available in the simulation
  integer :: nmon_used ! number of calendar months used included in the annual varlues
  integer :: month2_up,month2o_up,month2s_up  ! for figuring out which months to use ...
  integer :: nyear_sim,nyear_obs ! number of simulated and observed years that should be
                                 ! available if there is no missing data     
  integer :: nmon_sim ! length of the simulation period in months (based on the namelist parameters)
  integer :: mon,m1,m2 ! loop variables for calculating annual values
  integer :: nmon_count ! count of total number of relevant months, just to find the number of years ...
  integer :: first_month ! the month starting each "year" in the t-test
  character*200 :: headerline,dataline
  
  namelist/param/files,year1s,month1s,year2s,month2s,&
       fileo,year1o,month1o,year2o,month2o,&
       month1,month2,hour1,hour2,hour_step,&
       outfile,outfile_grads,lgrads,miss,&
       l_code_in_char,&
       min_fraction_valid_data

  miss=-9.99e6
  min_fraction_valid_data=0.5  
  read(*,nml=param)
  
!  write(*,*)'year1s,month1s,year2s,month2s',year1s,month1s,year2s,month2s
!  write(*,*)'year1o,month1o,year2o,month2o',year1o,month1o,year2o,month2o
!  write(*,*)'month1,month2',month1,month2
  
  nhour_anal=(hour2-hour1)/hour_step+1
!
!  Which calendar months will be used in the t test?  
!  
  month_used=.false.
  month_sim=.true.

  month2_up=month2
  if(month2.lt.month1)month2_up=month2+nmonth
  month2s_up=month2s
  if(month2s.lt.month1s)month2s_up=month2s+nmonth
  !  
  ! Calculate the length of the simulation in months.
  !
  nmon_sim=(nmonth*(year2s-year1s)+month2s-month1s+1)
  !
  ! If the simulation is less than one year: set month_sim to .false. for months
  ! that are not included in the simulation 
  !
  if(nmon_sim.lt.nmonth)then
     do mon=1,nmonth
        if((year2s.eq.year1s).and.(mon.lt.month1s.or.mon.gt.month2s))month_sim(mon)=.false.
        if((year2s.gt.year1s).and.(mon.gt.month2s.and.mon.lt.month1s))month_sim(mon)=.false.        
     enddo
  endif
  !   
  ! Calculate the number of calendar months included in the annual values 
  !
  nmon_used=0
  do month=month1,month2_up
     mon=1+mod(month-1,nmonth)
     month_used(mon)=month_sim(mon)
     if(month_used(mon))nmon_used=nmon_used+1
  enddo
  write(*,*)'Number of months used:',nmon_used
  if(nmon_used.lt.1)then
     write(*,*)'Nothing to calculate. Program terminates.'
     stop
  endif
  
!------------------------------------------------------------------------
! Read the observed and simulated time series from files 
!------------------------------------------------------------------------
  
  obs=miss
  sim=miss
  
  open(unit=1,file=trim(fileo),form='formatted',status='old')
  open(unit=2,file=trim(files),form='formatted',status='old')
!
! Read the monthly means from the observation file 
!
  read(1,*,end=3) ! the first line is a header
  do while (.true.)
  if(l_code_in_char)then    
1    read(1,*,err=1,end=3)station_char,longitude,latitude,year,month,hour,&
          variable,f1
  else
2    read(1,*,err=2,end=3)station_int,longitude,latitude,year,month,hour,&
          variable,f1
  endif   
     if(abs(f1).lt.abs(miss))then
     if((year.gt.year1o.or.(year.eq.year1o.and.month.ge.month1o)).and.&
        (year.lt.year2o.or.(year.eq.year2o.and.month.le.month2o)))then        
        obs(hour+1,month,1+year-year1o)=f1
     endif
  endif     
  enddo
3 continue  
  close (1)
!--------------------------------------------------------------------------
! Read the monthly means from the simulation file 
!--------------------------------------------------------------------------
  
  read(2,*,end=13) ! the first line is a header
  do while (.true.)
  if(l_code_in_char)then   
11   read(2,*,err=11,end=13)station_char,longitude,latitude,year,month,hour,&
          variable,f1
  else
12   read(2,*,err=12,end=13)station_int,longitude,latitude,year,month,hour,&
          variable,f1
  endif
  
  if(abs(f1).lt.abs(miss))then
     if((year.gt.year1s.or.(year.eq.year1s.and.month.ge.month1s)).and.&
        (year.lt.year2s.or.(year.eq.year2s.and.month.le.month2s)))then        
        sim(hour+1,month,1+year-year1s)=f1
     endif
  endif     
  enddo
13 continue  
  close (2)

!----------------------------------------------------------------------
!  Calculate the all-UTC monthly means for observations
!  (only when data is available for all the required hours!)  
!----------------------------------------------------------------------
  do year=year1o,year2o
     do month=1,nmonth
        valid=.true.
        f1=0.
        do hour=hour1,hour2,hour_step
           if(abs(obs(hour+1,month,1+year-year1o)).lt.abs(miss))then
              f1=f1+obs(hour+1,month,1+year-year1o)
           else
              valid=.false.
           endif
        enddo
        if(valid)then
           obs(nhour+1,month,1+year-year1o)=f1/nhour_anal
        endif        
     enddo
  enddo
!----------------------------------------------------------------------
!  Calculate the all-UTC monthly means for the simulation
!  (only when data is available for all the required hours!)  
!----------------------------------------------------------------------  
  do year=year1s,year2s
     do month=1,nmonth
        valid=.true.
        f1=0.
        do hour=hour1,hour2,hour_step
           if(abs(sim(hour+1,month,1+year-year1s)).lt.abs(miss))then
              f1=f1+sim(hour+1,month,1+year-year1s)
           else
              valid=.false.
           endif
        enddo
        if(valid)then
           sim(nhour+1,month,1+year-year1s)=f1/nhour_anal
        endif        
     enddo
  enddo
  
!---------------------------------------------------------------------  
! Calculate the annual values for the observations and the simulations 
!---------------------------------------------------------------------  
  sim_ann=0
  obs_ann=0
!  
! How many years (or partial years) are availble for the simulation?  
!
! Brute force calculation: loop over all months in the simulation period, 
! calculate the number of used months, and divide by the annual number of used months
! (it should be possible to do this in a more intelligent way!)
  
  nmon_count=0
  do year=year1s,year2s
     m1=1
     m2=nmonth
     if(year.eq.year1s)m1=month1s
     if(year.eq.year2s)m2=month2s
     do month=m1,m2
        if(month_used(month))then
           nmon_count=nmon_count+1
           if(nmon_count.eq.1)first_month=month  ! the month starting each year
        endif   
     enddo
  enddo
  nyear_sim=nmon_count/nmon_used
!  
! The same for the observations. However, those months in the first year
! that precede the first month of a t-test year are excluded because
! the do not belong to a full year  
  
  nmon_count=0
  do year=year1o,year2o
     m1=1
     m2=nmonth     
     if(year.eq.year1o)m1=max(month1o,first_month)
     if(year.eq.year2o)m2=month2o
     do month=m1,m2
        if(year.gt.year1o.or.month.ge.month1s)then
           if(month_used(month))nmon_count=nmon_count+1
        endif        
     enddo
  enddo
  nyear_obs=nmon_count/nmon_used

!
! Calculate the 'annual' values used in the t-test
! for each UTC hour analyzed
  
  obs_ann=0.
  sim_ann=0.
  do j=1,nhour_anal+1  ! the all-UTC values are included in the same loop
     if(j.le.nhour_anal)then        
        hour=hour1+(j-1)*hour_step
     else
        hour=nhour
     endif
!
! Annual means for the observations
!          
     do i=1,nyear_obs
        year=year1o+(i-1) 
        if(first_month.lt.month1o)year=year+1                
        nmon_count=0  ! number of months in a year with non-missing data
        do month=first_month,first_month+nmon_used-1
           if(month.gt.nmonth)year=year+1           
           mon=1+mod(month-1,nmonth)
           f1=obs(hour+1,mon,1+year-year1o)
           if(abs(f1).lt.abs(miss))then
              obs_ann(i,hour+1)=obs_ann(i,hour+1)+f1
              nmon_count=nmon_count+1
           endif           
        enddo
        if(nmon_count.ge.(min_fraction_valid_data*nmon_used))then
           obs_ann(i,hour+1)=obs_ann(i,hour+1)/nmon_count
        else
           obs_ann(i,hour+1)=miss
        endif        
     enddo
!
! Annual means for the simulation
!     
     do i=1,nyear_sim
        year=year1s+(i-1) 
        if(first_month.lt.month1s)year=year+1        
        nmon_count=0  ! number of months in year with non-missing data
        do month=first_month,first_month+nmon_used-1
           if(month.gt.nmonth)year=year+1           
           mon=1+mod(month-1,nmonth)
           f1=sim(hour+1,mon,1+year-year1s)
           if(abs(f1).lt.abs(miss))then
              sim_ann(i,hour+1)=sim_ann(i,hour+1)+f1
              nmon_count=nmon_count+1
           endif           
        enddo
        if(nmon_count.ge.(min_fraction_valid_data*nmon_used))then
           sim_ann(i,hour+1)=sim_ann(i,hour+1)/nmon_count
        else
           sim_ann(i,hour+1)=miss
        endif        
     enddo    

  enddo ! end of the (j -> hour) loop

!-----------------------------------------------------------------------  
! Conduct the t test
!-----------------------------------------------------------------------  

  do j=1,nhour_anal+1  ! the all-UTC values are included in the same loop
     if(j.le.nhour_anal)then        
        hour=hour1+(j-1)*hour_step
     else
        hour=nhour
     endif
     
     call test_statistic(t(hour+1),dof(hour+1),miss,&
          sim_ann(1,hour+1),nyear_sim,obs_ann(1,hour+1),nyear_obs)
     call pval(t(hour+1),p_value(hour+1),dof(hour+1),miss)
     
  enddo 

!-----------------------------------------------------------------  
! Output to text file 
!-----------------------------------------------------------------
 
   open(unit=1,file=trim(outfile),form='formatted')

   if(l_code_in_char)then
   
   headerline='station@hdr:string longitude@hdr:real latitude@hdr:real'//&
              ' hour@hdr:integer variable@hdr:integer dof@body:integer'//&
              ' t@body:real p_value@body:real'
   else
   headerline='station@hdr:integer longitude@hdr:real latitude@hdr:real'//&
              ' hour@hdr:integer variable@hdr:integer dof@body:integer'//&
              ' t@body:real p_value@body:real'   
   endif

   write(1,*)trim(headerline)
   do j=1,nhour_anal+1
     if(j.le.nhour_anal)then        
        hour=hour1+(j-1)*hour_step
     else
        hour=nhour
     endif
     if(l_code_in_char)then
     write(1,'(A3,2F16.6,1X,I2,1X,I3,1X,I3,2F16.6)')&
          station_char,longitude,latitude,hour,variable,dof(hour+1),t(hour+1),p_value(hour+1)
   else
     write(1,'(I6,2F16.6,1X,I2,1X,I3,1X,I3,2F16.6)')&
          station_int,longitude,latitude,hour,variable,dof(hour+1),t(hour+1),p_value(hour+1)
   endif
  
   enddo  
   close(1)
!-------------------------------------------------------------
!  Output to GrADS binary file?
!-------------------------------------------------------------
   if(lgrads)then
      open(unit=1,form='unformatted',file=trim(outfile_grads),access='direct',recl=4)
      if(l_code_in_char)station_int=0
      write(1,rec=1)real(station_int) ! will be zero if station code is a character string
      write(1,rec=2)longitude
      write(1,rec=3)latitude            
      do j=1,nhour_anal+1
        if(j.le.nhour_anal)then        
           hour=hour1+(j-1)*hour_step
        else
          hour=nhour
        endif
        write(1,rec=3+j)real(dof(hour+1))
        write(1,rec=3+(nhour_anal+1)+j)t(hour+1)
        write(1,rec=3+2*(nhour_anal+1)+j)p_value(hour+1)            
     enddo
   endif

!----------------------------------------------------------------   
end program t_test
!----------------------------------------------------------------

subroutine test_statistic(t,dof,miss,f1,n1,f2,n2)
  !
  ! Calculation of test statistic and dof for standard
  ! two-sample t test
  !
  implicit none
  real :: t  ! t test value (out)
  integer :: dof ! number of degrees of freedom (out)
  real :: miss ! missing value code (in / out)
  integer :: n1,n2 ! mumber of years in samples 1 and 2 (in)
  real :: f1(n1),f2(n2) ! time series in samples 1 and 2 (in)
  real :: mean1,mean2,var1,var2 ! mean values and variances
  integer :: n1_real,n2_real ! numbers of years with non-missing data
  integer :: i
  mean1=0.
  mean2=0.
  var1=0.
  var2=0.
  n1_real=0.
  n2_real=0.
!
! Calculate mean and variance for time series 1
!  
  do i=1,n1
     if(abs(f1(i)).lt.abs(miss))then
        n1_real=n1_real+1
        mean1=mean1+f1(i)  ! division with number of years later
        var1=var1+f1(i)**2. ! conversion to true variance later
     endif
  enddo
  if(n1_real.ge.1)then
     mean1=mean1/n1_real 
     var1=var1/n1_real-mean1**2. ! variance with n in denominator 
  endif                          ! (cf. calculation of t later)  
!
! Calculate mean and variance for time series 2
!  
  do i=1,n2
     if(abs(f2(i)).lt.abs(miss))then
        n2_real=n2_real+1
        mean2=mean2+f2(i)
        var2=var2+f2(i)**2.
     endif
  enddo
  if(n2_real.ge.1)then
     mean2=mean2/n2_real
     var2=var2/n2_real-mean2**2.
  endif
!
! Number of degrees of freedom 
!  
  dof=n1_real+n2_real-2
!
! t value
!  
  if(dof.ge.1.and.n1_real.ge.1.and.n2_real.ge.1.and.&
      (n1_real*var1+n2_real*var2.gt.0))then          
      t=(mean1-mean2)/sqrt((n1_real*var1+n2_real*var2)/dof)&
          /sqrt(1./n1_real+1./n2_real)
  else
     t=miss
     dof=0
  endif

!  write(*,*)'f1:',(f1(i),i=1,n1)
!  write(*,*)'f2:',(f2(i),i=1,n2)  
!  write(*,*)'mean1,var1,n1_real',mean1,var1,n1_real
!  write(*,*)'mean2,var2,n2_real',mean2,var2,n2_real  
!  write(*,*)'t,dof',t,dof
  
  return
  end subroutine test_statistic

  subroutine pval(t,p,dof,miss)
    !
    ! Approximate p-value table for t test, based on tabulated critical values.
    !
    ! The values run from 0 (highly negative t) to 1 (highly positive t)
    !
    ! if the t value is missing, the p-value is also set as missing
    
    implicit none
    real :: t ! t-value (in)
    real :: p ! p-value (out)
    integer :: dof ! number of degrees of freedom (in)
    real :: miss ! missing value code (in, out)
    integer :: dof2 ! dof truncated to dofmax=120
    integer,parameter :: dof_max=120
    integer,parameter :: n_threshold=11
    real :: tcrit(n_threshold,dof_max)
    real :: plev(n_threshold)
    integer :: i,j
    data plev /0.75,0.8,0.85,0.9,0.95,0.975,0.99,0.995,0.9975,0.999,0.9995/ 
    data (tcrit(i,1),i=1,n_threshold)&
         /1.000,1.376,1.963,3.078,6.314,12.706,31.821,63.567,127.321,318.309,636.19/
    data (tcrit(i,2),i=1,n_threshold)&
         /0.816,1.061,1.386,1.886,2.920,4.303,6.965,9.925,14.089,22.327,31.599/
    data (tcrit(i,3),i=1,n_threshold)&
         /0.765,0.978,1.250,1.638,2.353,3.182,4.541,5.841,7.453,10.215,12.924/
    data (tcrit(i,4),i=1,n_threshold)&
         /0.741,0.941,1.190,1.533,2.132,2.776,3.747,4.604,5.598,7.173,8.610/
    data (tcrit(i,5),i=1,n_threshold)&
         /0.727,0.920,1.156,1.476,2.015,2.571,3.365,4.032,4.773,5.893,6.869/
    data (tcrit(i,6),i=1,n_threshold)&
         /0.718,0.906,1.134,1.440,1.943,2.447,3.143,3.707,4.317,5.208,5.959/
    data (tcrit(i,7),i=1,n_threshold)&
         /0.711,0.896,1.119,1.415,1.895,2.365,2.998,3.499,4.029,4.785,5.408/
    data (tcrit(i,8),i=1,n_threshold)&
         /0.706,0.889,1.108,1.397,1.860,2.306,2.896,3.355,3.833,4.501,5.041/
    data (tcrit(i,9),i=1,n_threshold)&
         /0.703,0.883,1.100,1.383,1.833,2.262,2.821,3.250,3.690,4.297,4.781/
    data (tcrit(i,10),i=1,n_threshold)&
         /0.700,0.879,1.093,1.372,1.812,2.228,2.764,3.169,3.581,4.144,4.587/
    data (tcrit(i,11),i=1,n_threshold)&
         /0.697,0.876,1.088,1.363,1.796,2.201,2.718,3.106,3.497,4.025,4.437/    
    data (tcrit(i,12),i=1,n_threshold)&
         /0.695,0.873,1.083,1.356,1.782,2.179,2.681,3.055,3.428,3.930,4.318/    
    data (tcrit(i,13),i=1,n_threshold)&
         /0.694,0.870,1.079,1.350,1.771,2.160,2.650,3.012,3.372,3.852,4.221/    
    data (tcrit(i,14),i=1,n_threshold)&
         /0.692,0.868,1.076,1.345,1.761,2.145,2.624,2.977,3.326,3.787,4.140/    
    data (tcrit(i,15),i=1,n_threshold)&
         /0.691,0.866,1.074,1.341,1.753,2.131,2.602,2.947,3.286,3.733,4.073/    
    data (tcrit(i,16),i=1,n_threshold)&
         /0.690,0.865,1.071,1.337,1.746,2.120,2.583,2.921,3.252,3.686,4.015/    
    data (tcrit(i,17),i=1,n_threshold)&
         /0.689,0.863,1.069,1.333,1.740,2.110,2.567,2.898,3.222,3.646,3.965/    
    data (tcrit(i,18),i=1,n_threshold)&
         /0.688,0.862,1.067,1.330,1.734,2.101,2.552,2.878,3.197,3.610,3.922/    
    data (tcrit(i,19),i=1,n_threshold)&
         /0.688,0.861,1.066,1.328,1.729,2.093,2.539,2.861,3.174,3.579,3.883/    
    data (tcrit(i,20),i=1,n_threshold)&
         /0.687,0.860,1.064,1.325,1.725,2.086,2.528,2.845,3.153,3.552,3.850/    
    data (tcrit(i,21),i=1,n_threshold)&
         /0.686,0.859,1.063,1.323,1.721,2.080,2.518,2.831,3.135,3.527,3.819/    
    data (tcrit(i,22),i=1,n_threshold)&
         /0.686,0.858,1.061,1.321,1.717,2.074,2.508,2.819,3.119,3.505,3.792/    
    data (tcrit(i,23),i=1,n_threshold)&
         /0.685,0.858,1.060,1.319,1.714,2.069,2.500,2.807,3.104,3.485,3.767/    
    data (tcrit(i,24),i=1,n_threshold)&
         /0.685,0.857,1.059,1.318,1.711,2.064,2.492,2.797,3.091,3.467,3.745/    
    data (tcrit(i,25),i=1,n_threshold)&
         /0.684,0.856,1.058,1.316,1.708,2.060,2.485,2.787,3.078,3.450,3.725/    
    data (tcrit(i,26),i=1,n_threshold)&
         /0.684,0.856,1.058,1.315,1.706,2.056,2.479,2.779,3.067,3.435,3.707/    
    data (tcrit(i,27),i=1,n_threshold)&
         /0.684,0.856,1.057,1.314,1.703,2.052,2.473,2.771,3.057,3.421,3.690/    
    data (tcrit(i,28),i=1,n_threshold)&
         /0.683,0.855,1.056,1.313,1.701,2.048,2.467,2.763,3.047,3.408,3.674/    
    data (tcrit(i,29),i=1,n_threshold)&
         /0.683,0.854,1.055,1.311,1.699,2.045,2.462,2.756,3.038,3.396,3.659/    
    data (tcrit(i,30),i=1,n_threshold)&
         /0.683,0.854,1.055,1.310,1.697,2.042,2.457,2.750,3.030,3.385,3.646/    
    data (tcrit(i,40),i=1,n_threshold)&
         /0.681,0.851,1.050,1.303,1.684,2.021,2.423,2.704,2.971,3.307,3.551/    
    data (tcrit(i,50),i=1,n_threshold)&
         /0.679,0.849,1.047,1.299,1.676,2.009,2.403,2.678,2.937,3.261,3.496/    
    data (tcrit(i,60),i=1,n_threshold)&
         /0.679,0.848,1.045,1.296,1.671,2.000,2.390,2.660,2.915,3.232,3.460/    
    data (tcrit(i,80),i=1,n_threshold)&
         /0.678,0.846,1.043,1.292,1.664,1.990,2.374,2.639,2.887,3.195,3.416/    
    data (tcrit(i,100),i=1,n_threshold)&
         /0.677,0.845,1.042,1.290,1.660,1.984,2.364,2.626,2.871,3.174,3.390/    
    data (tcrit(i,120),i=1,n_threshold)&
         /0.677,0.845,1.041,1.289,1.658,1.980,2.358,2.617,2.860,3.160,3.373/    
!
!   Linear interpolation of critical values for dof > 30    
!    
    do j=1,n_threshold
       do i=31,39
          tcrit(j,i)=tcrit(j,30)+(i-30.)/10.*(tcrit(j,40)-tcrit(j,30))
       enddo
       do i=41,49
          tcrit(j,i)=tcrit(j,40)+(i-40.)/10.*(tcrit(j,50)-tcrit(j,40))
       enddo
       do i=51,59
          tcrit(j,i)=tcrit(j,50)+(i-50.)/10.*(tcrit(j,60)-tcrit(j,50))
       enddo
       do i=61,79
          tcrit(j,i)=tcrit(j,60)+(i-60.)/20.*(tcrit(j,80)-tcrit(j,60))
       enddo
       do i=81,99
          tcrit(j,i)=tcrit(j,80)+(i-80.)/20.*(tcrit(j,100)-tcrit(j,80))
       enddo
       do i=101,119
          tcrit(j,i)=tcrit(j,100)+(i-100.)/20.*(tcrit(j,120)-tcrit(j,100))
       enddo       
    enddo

    dof2=min(dof,120)
    p=miss
    if(abs(t).lt.abs(miss))then
       p=0.501
       do i=1,n_threshold
         if(abs(t).gt.tcrit(i,dof))p=plev(i)
       enddo
       if(t.lt.0)p=1-p
!       write(*,*)'t,dof,p',t,dof,p
    endif
    return
    end subroutine pval
    
