PROGRAM LCBIN ! John Southworth jkt~astro.keele.ac.uk ! Astrophysics Group, Keele University, UK ! This program performs basic actions on on a set of data consisting ! of independent and dependent variables (possibly with errorbars). ! It is intended to work on light curves of variable stars. ! Capibilities include: phasing, sorting, binning consecutive dtapoints, ! binning into even time bins, weighted binning, binning into specified ! uneven time bins, clipping outliers from data, splitting datafiles. ! Version 1 (2002/12/03): Much written, but not completed ! Version 2 (2010/09/07): Extended and debugged ! Version 3 (2010/09/17): Added the SPLIT procedure ! Version 4 (2012/04/09): Sorted out unweighted binning errors !======================================================================= implicit none character*5 ACTION ! What the program must undertake character*1 ERRORS ! Whether the data has got errors character*30 INFILE,OUTFILE ! Input and output datafile names real*8 DAT(3,99999) ! Time, Mag, Error, Phase & Cycle real*8 BINNED(3,99999) ! Normal points created from data integer NUMDATA,NUMNORM ! Num (datapoints, normal points) integer i,j,k,ERROR ! Loop counters and an error flag character*80 STRING ! String variable to read data in real*8 TZERO,PERIOD,TOLER,BINSIZE,CLIP integer NUMBIN,NFILE do i = 1,99999 do j = 1,3 DAT(j,i) = 0.0d0 BINNED(j,i) = 0.0d0 end do end do NUMDATA = 0 NUMNORM = 0 write (*,*) " " write (*,'(A40,A40)') "LCBIN Phase and bin time-series data ", & "(John Southworth, jkt~astro.keele.ac.uk)" 200 continue write (*,'(A40,A40)') "Commands: 'read' 'phase' 'bin1' 'bin2' ", & "'ecbin' 'write' 'stats' 'exit' 'quit' " write (*,'(A40,A40)') "read FILENAME inputs data (w", & "ith or without errorbars) from FILENAME" write (*,'(A40,A40)') "phase TZERO PERIOD phases the data", & " using linear ephemeris TZERO and PERIOD" write (*,'(A40,A40)') "split NFILE splits the data", & "set line by line into NFILE new datasets" write (*,'(A40,A40)') "bin1 NUMBER TOLER CLIP bins NUMBER dat", & "apoints (within a maximum time of TOLER)" write (*,'(A40,A40)') "bin2 BINSIZE CLIP bins datapoints", & " into equal bins. CLIP is sigma clipping" write (*,'(A40,A40)') "ecbin CLIP bins datapoints", & "with different bin widths at diff phases" write (*,'(A40,A40)') "write FILENAME outputs binned ", & "data (or original data) to file FILENAME" !----------------------------------------------------------------------- do i = 1,10000 write (*,'(A3,$)') ">> " read (*,'(A80)') STRING read (STRING,*) ACTION if ( ACTION == "read" ) then read (STRING,*,iostat=ERROR) ACTION,INFILE if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read a filena", & "me from the inputted text. " else CALL READIN (DAT,NUMDATA,INFILE) endif else if ( ACTION == "phase" ) then read (STRING,*,iostat=ERROR) ACTION,TZERO,PERIOD if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read two floa", & "ting point numbers from the inputted text. " else if ( NUMDATA < 1 ) then write (*,'(A35,A45)') "#### ERROR: no data have been read ", & "in from a data file so no phasing can be done" else ! print*,"a ",dat(1,1),dat(2,1),dat(3,1) ! print*,"a ",dat(1,2),dat(2,2),dat(3,2) ! print*,"a ",dat(1,3),dat(2,3),dat(3,3) ! print*,"a ",dat(1,4),dat(2,4),dat(3,4) CALL PHASER (DAT,NUMDATA,TZERO,PERIOD) endif else if ( ACTION == "split" ) then read (STRING,*,iostat=ERROR) ACTION,NFILE if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read one inte", & "ger from the inputted text. " else if ( NUMDATA < 1 ) then write (*,'(A35,A45)') "#### ERROR: no data have been read ", & "in from a data file so no splitting can occur" else if ( NFILE < 2 .or. NFILE > 50 ) then write (*,'(A35,A45)') "#### ERROR: the number of files mus", & "t be between 2 and 50. " else CALL SPLIT (DAT,NUMDATA,NFILE) endif else if ( ACTION == "bin1" ) then read (STRING,*,iostat=ERROR) ACTION,NUMBIN,TOLER,CLIP if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read an integ", & "er and two floating point numbers from input." else if ( NUMDATA < 1 ) then write (*,'(A35,A45)') "#### ERROR: no data have been read ", & "in from a data file so no phasing can be done" else CALL SORTER (DAT,NUMDATA) CALL BIN1 (DAT,NUMDATA,NUMBIN,TOLER,CLIP,BINNED,NUMNORM) end if else if ( ACTION == "bin2" ) then read (STRING,*,iostat=ERROR) ACTION,BINSIZE,CLIP if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read in two f", & "loating point numbers from the inputted text." else if ( NUMDATA < 1 ) then write (*,'(A35,A45)') "#### ERROR: no data has been read i", & "n from a data file so no binning can be done." else CALL SORTER (DAT,NUMDATA) CALL BIN2 (DAT,NUMDATA,BINSIZE,CLIP,BINNED,NUMNORM) end if else if ( ACTION == "ecnor" ) then read (STRING,*,iostat=ERROR) ACTION,CLIP if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read in the f", & "loating point number from the inputted text. " else if ( NUMDATA < 1 ) then write (*,'(A35,A45)') "#### ERROR: no data has been read i", & "n from a data file so no binning can be done." else CALL SORTER (DAT,NUMDATA) CALL ECBIN (DAT,NUMDATA,CLIP,BINNED,NUMNORM) end if else if ( ACTION == "write" ) then read (STRING,*,iostat=ERROR) ACTION,OUTFILE if ( ERROR /= 0 ) then write (*,'(A35,A45)') "#### ERROR: unable to read a filena", & "me from the inputted text. " else CALL WRITEOUT (DAT,NUMDATA,BINNED,NUMNORM,OUTFILE) endif else if ( ACTION == "stats" ) then write (*,'(A34,A28,I5)') "## Number of datapoint which have ", & "been read from file: ",NUMDATA write (*,'(A34,A28,I5)') "## Number of binned/normal points ", & "which have been calculated: ",NUMNORM else if ( ACTION == "clear" ) then NUMDATA = 0 NUMNORM = 0 else if ( ACTION == "quit" .or. ACTION == "exit" .or. & ACTION == "q" .or. ACTION == "Q" ) then exit else GOTO 200 end if end do write (*,*) " " END PROGRAM LCBIN !======================================================================= !======================================================================= SUBROUTINE READIN (DAT,NUMDATA,INFILE) implicit none real*8 DAT(3,99999) ! Time,Mag,Error,Phase,Cycle integer NUMDATA ! Number of datapoints read in character ERRORS*1,STRING*80,INFILE*30 integer i,j,ERROR open (14,file=INFILE,status="old",iostat=ERROR) if ( ERROR /= 0 ) then write (*,'(A29,A17,A30)') "#### ERROR: unable to open th", & "e input datafile ",INFILE RETURN end if read (14,'(A80)') STRING read (STRING,*,iostat=ERROR) (DAT(j,1),j=1,3) rewind (14) if ( ERROR == 0 ) then ERRORS = "y" write (*,'(A39,A41)') "## Message: measurement errors have bee", & "n detected in column 3 in the input file." else read (STRING,*,iostat=ERROR) (DAT(j,1),j=1,2) if ( ERROR == 0 ) then ERRORS = "n" write (*,'(A37,A43)') "## Message: no observational errors f", & "ound. Will calculate sigmas when binning up" else write (*,'(A37,A44)') "#### ERROR: unable to understand the ", & "first line of the input file. No data read." RETURN endif endif do i = 1,99999 if ( ERRORS == "y" ) then read (14,*,iostat=ERROR) (DAT(j,i),j=1,3) ! Don't want zero DAT(3,i) = max(abs(DAT(3,i)),1.0d-7) ! or negative errors else read (14,*,iostat=ERROR) (DAT(j,i),j=1,2) DAT(3,i) = -1.0d0 ! Make it obvious that there end if ! are not observational errors if ( ERROR /= 0 ) exit end do NUMDATA = i - 1 write (*,'(A17,I6,A28,A30)') "## Message: read ", NUMDATA, & " datapoints from input file ",INFILE if (NUMDATA >= 99999) write(*,'(A21,A59)')"### Warning: the inpu", & "t array is full. Any further data won't be read in or used." close (14,status="keep") END SUBROUTINE READIN !======================================================================= SUBROUTINE WRITEOUT (DAT,NUMDATA,BINNED,NUMNORM,OUTFILE) implicit none real*8 DAT(3,99999),BINNED(3,99999) integer NUMDATA,NUMNORM,i,j,ERROR character OUTFILE*30 ERROR = 0 open (15,file=OUTFILE,status="new",iostat=ERROR) if ( ERROR /= 0 ) then write (*,'(A29,A18,A30)') "#### ERROR: unable to open th", & "e output datafile ",OUTFILE RETURN end if if ( NUMNORM > 0 ) then do i = 1,NUMNORM write (15,'(F18.8,1X,F12.8,1X,F11.8)') (BINNED(j,i),j=1,3) end do write (*,'(A20,I5,A23,A30)') "## Message: written ", & NUMNORM," normal points to file ",OUTFILE else do i = 1,NUMDATA if ( DAT(3,i) > 0.0d0 ) then write (15,'(F18.8,1X,F12.8,1X,F11.8)') (DAT(j,i),j=1,3) else write (15,'(F18.8,1X,F12.8,1X,F11.8)') (DAT(j,i),j=1,2) end if end do write (*,'(A20,I5,A20,A30)') "## Message: written ", & NUMDATA," datapoints to file ",OUTFILE end if close (15,status="keep") END SUBROUTINE WRITEOUT !======================================================================= SUBROUTINE PHASER (DAT,NUMDATA,TZERO,PERIOD) implicit none real*8 DAT(3,99999) ! Time Mag Error Phase Cycle real*8 TZERO,PERIOD ! Ephemeris to use for phasing real*8 EPOCH ! Number of orbits since TZERO integer NUMDATA ! Number of datapoints to use integer i,j,ERROR ! Loop counters and error flag ! print*,"b ",dat(1,1),dat(2,1),dat(3,1) ! print*,"b ",dat(1,2),dat(2,2),dat(3,2) ! print*,"b ",dat(1,3),dat(2,3),dat(3,3) ! print*,"b ",dat(1,4),dat(2,4),dat(3,4) do i = 1,NUMDATA EPOCH = (DAT(1,i) - TZERO) / PERIOD DAT(1,i) = EPOCH - int(EPOCH) if ( DAT(1,i) < 0.0d0 ) DAT(1,i) = DAT(1,i) + 1.0d0 end do write (*,'(A22,A21,F14.6,A2,F17.11,A4)') "## Message: data phase", & "d using Min(I) = JD ",TZERO," +",PERIOD," x E" END SUBROUTINE PHASER !======================================================================= SUBROUTINE SORTER (ra,n) ! Sorts observations in time implicit none ! using HEAPSORT from N.R. integer n ! Number of datapoints real*8 ra(3,99999) ! Data (time,magnitude,error) real*8 rra1,rra2,rra3 integer i,ir,j,l if (n.lt.2) return l=n/2+1 ir=n 10 continue if(l.gt.1)then l=l-1 ! rra=ra(l) rra1=ra(1,l) rra2=ra(2,l) rra3=ra(3,l) else ! rra=ra(ir) rra1=ra(1,ir) rra2=ra(2,ir) rra3=ra(3,ir) ! ra(ir)=ra(1) ra(1,ir)=ra(1,1) ra(2,ir)=ra(2,1) ra(3,ir)=ra(3,1) ir=ir-1 if(ir.eq.1)then ! ra(1)=rra ra(1,1)=rra1 ra(2,1)=rra2 ra(3,1)=rra3 return endif endif i=l j=l+l 20 if(j.le.ir)then if(j.lt.ir)then ! if(ra(j).lt.ra(j+1))j=j+1 if(ra(1,j).lt.ra(1,j+1))j=j+1 endif ! if(rra.lt.ra(j))then if(rra1.lt.ra(1,j))then ! ra(i)=ra(j) ra(1,i)=ra(1,j) ra(2,i)=ra(2,j) ra(3,i)=ra(3,j) i=j j=j+j else j=ir+1 endif goto 20 endif ! ra(i)=rra ra(1,i)=rra1 ra(2,i)=rra2 ra(3,i)=rra3 goto 10 ! do i = 1,NUMDATA ! TAG = i ! do j = i+1,NUMDATA ! if ( DAT(1,j) < DAT(1,i) ) TAG = j ! end do ! do j = 1,3 ! STORE = DAT(j,TAG) ! DAT(j,TAG) = DAT(j,i) ! DAT(j,i) = STORE ! end do ! end do END SUBROUTINE SORTER !======================================================================= SUBROUTINE SPLIT (DAT,NUMDATA,NFILE) implicit none real*8 DAT(3,99999) ! Time, Observation, Error integer NUMDATA ! Number of datapoints integer NFILE ! Number of output files character*30 FILES(NFILE) ! Names of output files integer COWNT(NFILE) ! Number of datapoints output integer i,j,k,ERROR ! Loop counters and error flag do i = 1,NFILE COWNT(i) = 0 600 continue write (*,'(A23,I2,A4,I2,A4,$)') & "Enter the name of file ",i," of ",NFILE," >> " read (*,*,iostat=ERROR) FILES(i) if ( ERROR /= 0 ) then write(*,'(A43)') "Could not read file name. Please try again." goto 600 end if open (49+i,file=FILES(i),status="new",iostat=ERROR) if ( ERROR /= 0 ) then write (*,'(A29,A14,A30)') "#### ERROR: unable to open th", & "e output file ",FILES(i) goto 600 end if end do k = 0 do i = 1,NUMDATA k = k + 1 if ( k > NFILE ) k = 1 if ( DAT(3,i) > 0.0d0 ) then write (49+k,'(F18.8,1X,F12.8,1X,F11.8)') (DAT(j,i),j=1,3) else write (49+k,'(F18.8,1X,F12.8,1X,F11.8)') (DAT(j,i),j=1,2) end if COWNT(k) = COWNT(k) + 1 end do do i = 1,NFILE write (*,'(A20,I5,A20,A30)') "## Message: written ", & COWNT(i)," datapoints to file ",FILES(i) close (49+i,status="keep") end do END SUBROUTINE SPLIT !======================================================================= SUBROUTINE BIN1 (DAT,NUMDATA,NUMBIN,TOLER,CLIP,BINNED,NUMNORM) ! This subroutine combines NUMBIN adjacent datapoints into ! one point and calculates its error accordingly. Data must ! be sorted prior to this subroutine being called. implicit none real*8 DAT(3,99999) ! IN: Time Mag Error Phase Orbit integer NUMDATA ! IN: Number of datapoints integer NUMBIN ! IN: Number of points in each bin real*8 TOLER ! IN: Max separation of twinned data real*8 CLIP ! IN: Size of sigma clipping to use real*8 BINNED(3,99999) ! OUT: Normal points Time Mag Error integer NUMNORM ! OUT: Number of normal points integer i,j,k,l,m,n,o ! LOCAL: loop counters and error flag real*8 ARRAY(3,1000) if ( NUMBIN < 2 .or. NUMBIN > 1000 ) then write (*,'(A39,A41)') "#### ERROR: number of datapoints to go ", & "in a bin must be between 2 and 10000. " return end if m = 0 ! m is the incremental counter for BINNED j = 0 ! j is the incremental counter for DATA do i = 1,100000 ARRAY(1,1) = DAT(1,j+1) do k = 1,1000 ! i, k and l are just loop counters if ( k > NUMBIN ) exit j = j + 1 if ( j > NUMDATA ) exit if ( abs(DAT(1,j) - ARRAY(1,1)) > TOLER ) exit ARRAY(1,k) = DAT(1,j) ARRAY(2,k) = DAT(2,j) ARRAY(3,k) = DAT(3,j) end do m = m + 1 CALL CLIPPEDMEAN (ARRAY,k-1,CLIP,BINNED(1,m),BINNED(2,m), & BINNED(3,m)) if ( j >= NUMDATA ) exit end do NUMNORM = m write (*,'(A28,A11,I5,A7,I5,A13)') "## Message: number of normal", & " points is ",NUMNORM," (from ",NUMDATA," datapoints)." END SUBROUTINE BIN1 !======================================================================= SUBROUTINE BIN2 (DAT,NUMDATA,BINSIZE,CLIP,BINNED,NUMNORM) implicit none real*8 DAT(3,99999) ! Time, Mag, Error, Phase & Orbit real*8 BINNED(3,99999) ! Phase, Point, Error & Numpoints real*8 BINSIZE,CLIP integer NUMDATA,NUMNORM integer i,j,k,m,n,ERROR ! Loop counters and an error flag real*8 ARRAY(3,99999) real*8 LOWER,UPPER ! For each normal point, first define the lower and upper ! boundaries of the temporal range. Then accumulate data in ! that range into array ARRAY, which is then sent to subrou- ! tine CLIPPEDMEAN for the clipped and weighted mean of the ! dependent and independent variables (and the error) to be ! found. Put the results into array BINNED. k = 0 n = 1 NUMNORM = 0 do i = 1,99999 LOWER = DAT(1,1) + (i-1)*BINSIZE UPPER = LOWER + BINSIZE if ( LOWER >= DAT(1,NUMDATA) ) exit n = 0 do j = 1,NUMDATA k = k + 1 if ( DAT(1,k) >= LOWER .and. DAT(1,k) < UPPER ) then n = n + 1 ARRAY(1,n) = DAT(1,k) ARRAY(2,n) = DAT(2,k) ARRAY(3,n) = DAT(3,k) else exit end if end do if ( n > 0 ) then NUMNORM = NUMNORM + 1 CALL CLIPPEDMEAN (ARRAY,n,CLIP,BINNED(1,NUMNORM), & BINNED(2,NUMNORM),BINNED(3,NUMNORM)) end if end do write (*,'(A28,A11,I5,A7,I5,A13)') "## Message: number of normal", & " points is ",NUMNORM," (from ",NUMDATA," datapoints)." print*, lower, upper END SUBROUTINE BIN2 !======================================================================= SUBROUTINE ECBIN (DAT,NUMDATA,CLIP,BINNED,NUMNORM) implicit none real*8 DAT(3,99999) ! Time, Mag, Error, Phase & Orbit real*8 BINNED(3,99999) ! Phase, Point, Error & Numpoints real*8 CLIP integer NUMDATA,NUMNORM integer i,j,k,m,n,ERROR ! Loop counters and an error flag real*8 ARRAY(3,99999) real*8 time1,time2,binsize,lower,upper real*8 BINPOINTS(99999) integer NBIN ! For each normal point, first define the lower and upper ! boundaries of the temporal range. Then accumulate data in ! that range into array ARRAY, which is then sent to subrou- ! tine CLIPPEDMEAN for the clipped and weighted mean of the ! dependent and independent variables (and the error) to be ! found. Put the results into array BINNED. NBIN = 0 do i = 1,1000 write (*,'(A39,A41)') "Enter starting and ending times and bin", & " size (enter negative bin size to exit): " read (*,*) TIME1, TIME2, BINSIZE if ( BINSIZE < 0.000001d0 ) exit NBIN = NBIN + 1 BINPOINTS(NBIN) = TIME1 do j = 1,10000 if ( BINPOINTS(NBIN) < TIME2-BINSIZE*0.1d0 ) then NBIN = NBIN + 1 BINPOINTS(NBIN) = BINPOINTS(NBIN-1) + BINSIZE else exit end if end do end do ! open(35,file="bins.dat") ! do i=1,NBIN ! write(35,'(f12.8,f12.8)')binpoints(i),binpoints(i+1) ! enddo ! close(35) NUMNORM = 0 do i = 1,NBIN-1 LOWER = BINPOINTS(i) UPPER = BINPOINTS(i+1) n = 0 do j = 1,NUMDATA k = k + 1 if ( DAT(1,k) >= LOWER .and. DAT(1,k) < UPPER ) then n = n + 1 ARRAY(1,n) = DAT(1,k) ARRAY(2,n) = DAT(2,k) ARRAY(3,n) = DAT(3,k) else exit end if end do if ( n > 0 ) then NUMNORM = NUMNORM + 1 CALL CLIPPEDMEAN (ARRAY,n,CLIP,BINNED(1,NUMNORM), & BINNED(2,NUMNORM),BINNED(3,NUMNORM)) ! print*,n,BINNED(1,NUMNORM),BINNED(2,NUMNORM),BINNED(3,NUMNORM) end if end do write (*,'(A28,A11,I5,A7,I5,A13)') "## Message: number of normal", & " points is ",NUMNORM," (from ",NUMDATA," datapoints)." END SUBROUTINE ECBIN !======================================================================= SUBROUTINE CLIPPEDMEAN (DAT,NUMDATA,CLIP,WMEANI,WMEAND,WMEANE) ! This subroutine inputs a set of datapoints and iteratively ! rejects those greater than CLIP standard deviations away ! from the mean. It then calculates the weighted mean of the ! independent variable and the dependent variable, and the ! resulting error (1/sumofthesquares). implicit none integer NUMDATA ! IN: the number of datapoints real*8 DAT(3,1000) ! IN: the data to process real*8 CLIP ! IN: amount of sigma clipping to do real*8 WMEANI ! OUT: weighted mean of indep variable real*8 WMEAND ! OUT: weighted mean of dep variable real*8 WMEANE ! OUT: resulting error of WMEAND integer i,j,k,m,n ! LOCAL: loop counters real*8 SUMW,SUMWX,WMEAN,SIGMA ! LOCAL: statistical variables real*8 SUMM,SUMSQ,MEAN,DIFF integer PRINTOUT ! First calculate (unweighted) mean and standard deviation. PRINTOUT = 0 n = NUMDATA do i = 1,5 SUMM = 0.0d0 do j = 1,n SUMM = SUMM + DAT(2,j) end do MEAN = SUMM / n SUMM = 0.0d0 do j = 1,n SUMM = SUMM + (DAT(2,j)-MEAN)**2.0d0 end do SIGMA = sqrt(SUMM / (n - 1.0d0)) ! Now reject any points further than CLIP sigma from the mean do j = 1,NUMDATA if ( abs(DAT(2,j)-MEAN) > CLIP*SIGMA ) then if ( PRINTOUT == 0 ) then write (*,'(A22,$)') "Datapoints rejected: 1" PRINTOUT = 1 else write (*,'(A2,$)') " 1" end if if ( j < n ) then DAT(2,j) = DAT(2,n) DAT(1,j) = DAT(1,n) DAT(3,j) = DAT(3,n) n = n - 1 else n = n - 1 end if end if if ( n < 2 ) exit end do end do if ( PRINTOUT /= 0 ) write (*,'(A1)') " " ! Now calculate the weighted mean of the independent and ! dependent variables using the remaining points. if ( DAT(3,1) <= 0.0d0 ) then SUMM = 0.0d0 do j = 1,n SUMM = SUMM + DAT(1,j) end do WMEANI = SUMM / dble(n) SUMM = 0.0d0 do j = 1,n SUMM = SUMM + DAT(2,j) end do WMEAND = SUMM / dble(n) SUMM = 0.0d0 do j = 1,n SUMM = SUMM + (DAT(2,j)-WMEAND)**2.0d0 end do WMEANE = sqrt(SUMM / (dble(n) - 1.0d0)) / sqrt(dble(n)) else SUMW = 0.0d0 SUMWX = 0.0d0 do j = 1,n SUMW = SUMW + 1.0d0/DAT(3,j)**2.0d0 SUMWX = SUMWX + DAT(1,j)/DAT(3,j)**2.0d0 end do WMEANI = SUMWX / SUMW SUMW = 0.0d0 SUMWX = 0.0d0 do j = 1,n SUMW = SUMW + 1.0d0/DAT(3,j)**2.0d0 SUMWX = SUMWX + DAT(2,j)/DAT(3,j)**2.0d0 end do WMEAND = SUMWX / SUMW WMEANE = 0.0d0 do i = 1,n WMEANE = WMEANE + DAT(3,i)**2.0d0 end do WMEANE = sqrt(WMEANE) / dble(n) end if END SUBROUTINE CLIPPEDMEAN !======================================================================= !=======================================================================