C      ALGORITHM 749, COLLECTED ALGORITHMS FROM ACM.
C      THIS WORK PUBLISHED IN TRANSACTIONS ON MATHEMATICAL SOFTWARE,
C      VOL. 21, NO. 4, December, 1995, P.  372--378.
C
C This file contains 4 files separated by lines of the form
C         C*** filename
C
C The filenames in this file are:
C
C driver.f             driverd.f            fct.f               
C fctd.f                                                        
C
C*** driver.f
C**********************************************************************
C     DRIVER PROGRAM FOR TESTING FAST DISCRETE COSINE TRANSFORM
C
C     Allows the user to specify the length of transform desired,
C     and to perform a forward and inverse transform on one of two
C     possible data sequences: 
C        (i) random numbers between 0 and 1, or
C        (ii)a sequence representing one cycle of a sinusoid of
C            amplitude 10.
C     If the calls to FCT and IFCT are both successful, the ms error
C     between original and recovered data is printed out, and the actual
C     data is printed out if so desired.
C***********************************************************************
C
C     INTERNAL VARIABLES:
C       C         Array of cosine coefficients passed to FCT and IFCT.
C       DCTSAV    Array to save the DCT coefficients so that they can be
C                 printed out later.
C       I         DO loop counter used to address each array element in 
C                 turn.
C       ICH       When user is prompted to choose between different
C                 options, ICH holds his/her choice.
C       IFAULT    Fault indicator set by subroutines FCT and IFCT.
C       IX,IY,IZ  The three seeds for the random number generator.
C       F         Data array passed to subroutines FCT and IFCT.       
C       FSAVE     Array to save the original data so that it can be
C                 compared to the recovered data.
C       MAXN      Dimension of the arrays F, C, FSAVE and DCTSAV. Can be
C                 adjusted to suit the amount of memory available on the
C                 computer being used.
C       MAXPOW    Base 2 logarithm of maximum DCT length (see comments 
C                 to subroutine INIFCT.
C       N         Transform length.
C       PI        The constant 3.1415926...
C       SUM       Temporary variable to hold running sum during ms error
C                 calculation.
C       TEMP      Temporary variable used during ms error calculation to
C                 hold error between original and recovered data point.
C       TMSERR    Mean square error between original and recovered data.
C
C***********************************************************************
      PARAMETER(MAXPOW=20)
      PARAMETER(MAXN=10000)
      PARAMETER(PI=3.14159 26535 89793 23846)
      DIMENSION F(MAXN), C(MAXN), FSAVE(MAXN), DCTSAV(MAXN)
C
C     INITIALISE SEEDS FOR RANDOM NUMBER GENERATOR
C
      IX=1234
      IY=42
      IZ=1000
C
10    PRINT *
      PRINT *,'Enter N, the transform length:'
      READ *,N
      PRINT *,'N=',N
      IF(N.GT.MAXN) THEN
        PRINT *,'This N is larger than the dimension of the arrays used'
        PRINT *,'by DRIVER program.'
        GOTO 10
      ENDIF
C
20    PRINT *
      PRINT *,'1. Random test data'
      PRINT *,'2. Sinusoidal test data'
      PRINT *,'ENTER CHOICE:'
      READ *,ICH
      IF((ICH.GT.2).OR.(ICH.LT.1))GOTO 20
C
C     INITIALISE DATA AND SAVE IT FOR LATER
C
      DO 30 I=1,N
        IF(ICH.EQ.1)THEN
          F(I)=RANDOM(IX,IY,IZ)
        ELSEIF(ICH.EQ.2)THEN
          F(I)=10.0*SIN(2.0*PI*I/FLOAT(N))
        ENDIF
        FSAVE(I)=F(I)
30    CONTINUE
C
C     PERFORM THE FORWARD COSINE TRANSFORM
C
      PRINT *,'CALLING FCT...'
      CALL FCT(F,C,N,IFAULT)
C
C     SAVE TRANSFORMED DATA FOR LATER
C
      DO 40 I=1,N
        DCTSAV(I)=F(I)
40    CONTINUE
      PRINT *,'FCT has set IFAULT=',IFAULT
      IF(IFAULT.EQ.0)THEN
        PRINT *,'Successful completion of FCT'
      ELSEIF(IFAULT.EQ.1)THEN
        PRINT *,'ERROR -- N is <=1.'
        GOTO 10
      ELSEIF(IFAULT.EQ.2)THEN
        PRINT *,'ERROR -- N=',N,' is greater than 2**MAXPOW=',2**MAXPOW
        GOTO 10
      ELSEIF (IFAULT.EQ.3)THEN
        PRINT *,'ERROR -- N is not a power of two.'
        GOTO 10
      ELSE
        PRINT *,'ERROR -- Invalid IFAULT value (should never occur)'
        GOTO 10
      ENDIF
C
C     PERFORM THE INVERSE COSINE TRANSFORM
C
      PRINT *,'CALLING IFCT...'
      CALL IFCT(F,C,N,IFAULT)
      PRINT *,'IFCT has set IFAULT=',IFAULT
      IF(IFAULT.EQ.0)THEN
        PRINT *,'Successful completion of FCT'
      ELSEIF(IFAULT.EQ.1)THEN
        PRINT *,'ERROR -- N is <=1.'
        GOTO 10
      ELSEIF(IFAULT.EQ.2)THEN
        PRINT *,'ERROR -- N=',N,' is greater than 2**MAXPOW=',2**MAXPOW
        GOTO 10
      ELSEIF (IFAULT.EQ.3)THEN
        PRINT *,'ERROR -- N is not a power of two.'
        GOTO 10
      ELSE
        PRINT *, 'ERROR -- Invalid IFAULT value (should never occur)'
        GOTO 10
      ENDIF
C
C     CALCULATE MEAN-SQUARE ERROR BETWEEN ORIGINAL AND RECOVERED DATA
C
      SUM=0.0
      DO 50 I=1,N
        TEMP = F(I) - FSAVE(I)
        SUM=SUM+TEMP*TEMP
50    CONTINUE
      TMSERR=SUM/FLOAT(N)
      PRINT *
      PRINT *,'Mean square error=',TMSERR
C
      PRINT *
60    PRINT *,'0. Print out data'
      PRINT *,'1. Don''t print out data'
      PRINT *,'ENTER CHOICE'
      READ *,ICH
      IF((ICH.LT.0).OR.(ICH.GT.1)) GOTO 60
      IF (ICH.EQ.0)THEN
        PRINT *,
     +  '    I   ORIGINAL DATA   AFTER FCT          AFTER IFCT'
        DO 80 I=1,N
          PRINT 70,I,FSAVE(I),DCTSAV(I),F(I)
70        FORMAT(I6,F15.10,G20.10,F15.10)
80      CONTINUE

      PRINT *
      PRINT *,'Mean square error=',TMSERR
      ENDIF
C
      PRINT *
90    PRINT *,'0. Terminate program'
      PRINT *,'1. Continue'
      PRINT *,'ENTER CHOICE'
      READ *, ICH
      IF((ICH.LT.0).OR.(ICH.GT.1)) GOTO 90
      IF(ICH.EQ.1) GOTO 10
C
      STOP
      END




      FUNCTION RANDOM(IX,IY,IZ)
C
C     THIS IS THE WICHMANN & HILL RANDOM NUMBER GENERATOR
C     ALGORITHM AS-183, APPLIED STATISTICS VOL 31 NO 2, PP 188-190
C
      IX = 171 * MOD(IX, 177) - 2  * (IX /177)
      IY = 172 * MOD(IY, 176) - 35 * (IY/176)
      IZ = 170 * MOD(IZ, 178) - 63 * (IZ/178)
C
      IF(IX .LT. 0) IX = IX + 30269
      IF(IY .LT. 0) IY = IY + 30307
      IF(IZ .LT. 0) IZ = IZ + 30323
C
      RANDOM = AMOD(FLOAT(IX) / 30269.0 + FLOAT(IY) / 30307.0 +
     *              FLOAT(IZ) / 30323.0, 1.0)
      RETURN
      END

C*** driverd.f
C**********************************************************************
C     DRIVER PROGRAM FOR TESTING FAST DISCRETE COSINE TRANSFORM
C
C     Allows the user to specify the length of transform desired,
C     and to perform a forward and inverse transform on one of two
C     possible data sequences: 
C        (i) random numbers between 0 and 1, or
C        (ii)a sequence representing one cycle of a sinusoid of
C            amplitude 10.
C     If the calls to FCT and IFCT are both successful, the ms error
C     between original and recovered data is printed out, and the actual
C     data is printed out if so desired.
C***********************************************************************
C
C     INTERNAL VARIABLES:
C       C         Array of cosine coefficients passed to FCT and IFCT.
C       DCTSAV    Array to save the DCT coefficients so that they can be
C                 printed out later.
C       I         DO loop counter used to address each array element in 
C                 turn.
C       ICH       When user is prompted to choose between different
C                 options, ICH holds his/her choice.
C       IFAULT    Fault indicator set by subroutines FCT and IFCT.
C       IX,IY,IZ  The three seeds for the random number generator.
C       F         Data array passed to subroutines FCT and IFCT.       
C       FSAVE     Array to save the original data so that it can be
C                 compared to the recovered data.
C       MAXN      Dimension of the arrays F, C, FSAVE and DCTSAV. Can be
C                 adjusted to suit the amount of memory available on the
C                 computer being used.
C       MAXPOW    Base 2 logarithm of maximum DCT length (see comments 
C                 to subroutine INIFCT.
C       N         Transform length.
C       PI        The constant 3.1415926...
C       SUM       Temporary variable to hold running sum during ms error
C                 calculation.
C       TEMP      Temporary variable used during ms error calculation to
C                 hold error between original and recovered data point.
C       TMSERR    Mean square error between original and recovered data.
C
C***********************************************************************
      PARAMETER(MAXPOW=20)
      PARAMETER(MAXN=10000)
      DOUBLE PRECISION PI
      PARAMETER(PI=3.14159 26535 89793 23846D0)
      DOUBLE PRECISION F(MAXN), C(MAXN), FSAVE(MAXN), DCTSAV(MAXN)
      DOUBLE PRECISION TEMP, SUM, TMSERR
C
C     INITIALISE SEEDS FOR RANDOM NUMBER GENERATOR
C
      IX=1234
      IY=42
      IZ=1000
C
10     PRINT *
      PRINT *,'Enter N, the transform length:'
      READ *,N
      PRINT *,'N=',N
      IF(N.GT.MAXN) THEN
        PRINT *,'This N is larger than the dimension of the arrays used'
        PRINT *,'by DRIVER program.'
        GOTO 10
      ENDIF
C
20    PRINT *
      PRINT *,'1. Random test data'
      PRINT *,'2. Sinusoidal test data'
      PRINT *,'ENTER CHOICE:'
      READ *,ICH
      IF((ICH.GT.2).OR.(ICH.LT.1))GOTO 20
C
C     INITIALISE DATA AND SAVE IT FOR LATER
C
      DO 30 I=1,N
        IF(ICH.EQ.1)THEN
          F(I)=DBLE(RANDOM(IX,IY,IZ))
        ELSEIF(ICH.EQ.2)THEN
          F(I)=10.0D0*DSIN(2.0D0*PI*I/DBLE(N))
        ENDIF
        FSAVE(I)=F(I)
30    CONTINUE
C
C     PERFORM THE FORWARD COSINE TRANSFORM
C
      PRINT *,'CALLING FCT...'
      CALL FCT(F,C,N,IFAULT)
C
C     SAVE TRANSFORMED DATA FOR LATER
C
      DO 40 I=1,N
        DCTSAV(I)=F(I)
40    CONTINUE
      PRINT *,'FCT has set IFAULT=',IFAULT
      IF(IFAULT.EQ.0)THEN
        PRINT *,'Successful completion of FCT'
      ELSEIF(IFAULT.EQ.1)THEN
        PRINT *,'ERROR -- N is <=1.'
        GOTO 10
      ELSEIF(IFAULT.EQ.2)THEN
        PRINT *,'ERROR -- N=',N,' is greater than 2**MAXPOW=',2**MAXPOW
        GOTO 10
      ELSEIF (IFAULT.EQ.3)THEN
        PRINT *,'ERROR -- N is not a power of two.'
        GOTO 10
      ELSE
        PRINT *,'ERROR -- Invalid IFAULT value (should never occur)'
        GOTO 10
      ENDIF
C
C     PERFORM THE INVERSE COSINE TRANSFORM
C
      PRINT *,'CALLING IFCT...'
      CALL IFCT(F,C,N,IFAULT)
      PRINT *,'IFCT has set IFAULT=',IFAULT
      IF(IFAULT.EQ.0)THEN
        PRINT *,'Successful completion of FCT'
      ELSEIF(IFAULT.EQ.1)THEN
        PRINT *,'ERROR -- N is <=1.'
        GOTO 10
      ELSEIF(IFAULT.EQ.2)THEN
        PRINT *,'ERROR -- N=',N,' is greater than 2**MAXPOW=',2**MAXPOW
        GOTO 10
      ELSEIF (IFAULT.EQ.3)THEN
        PRINT *,'ERROR -- N is not a power of two.'
        GOTO 10
      ELSE
        PRINT *, 'ERROR -- Invalid IFAULT value (should never occur)'
        GOTO 10
      ENDIF
C
C     CALCULATE MEAN-SQUARE ERROR BETWEEN ORIGINAL AND RECOVERED DATA
C
      SUM=0.0D0
      DO 50 I=1,N
        TEMP = F(I) - FSAVE(I)
        SUM=SUM+TEMP*TEMP
50    CONTINUE
      TMSERR=SUM/DBLE(N)
      PRINT *
      PRINT *,'Mean square error=',TMSERR
C
      PRINT *
60    PRINT *,'0. Print out data'
      PRINT *,'1. Don''t print out data'
      PRINT *,'ENTER CHOICE'
      READ *,ICH
      IF((ICH.LT.0).OR.(ICH.GT.1)) GOTO 60
      IF (ICH.EQ.0)THEN
        PRINT *,
     +  '    I   ORIGINAL DATA       AFTER FCT             AFTER IFCT'
        DO 80 I=1,N
          PRINT 70,I,FSAVE(I),DCTSAV(I),F(I)
70        FORMAT(I6,D22.14,D22.14,D22.14)
80      CONTINUE
C
      PRINT *
      PRINT *,'Mean square error=',TMSERR
      ENDIF
C
      PRINT *
90    PRINT *,'0. Terminate program'
      PRINT *,'1. Continue'
      PRINT *,'ENTER CHOICE'
      READ *, ICH
      IF((ICH.LT.0).OR.(ICH.GT.1)) GOTO 90
      IF(ICH.EQ.1) GOTO 10
C
      STOP
      END




      FUNCTION RANDOM(IX,IY,IZ)
C
C     THIS IS THE WICHMANN & HILL RANDOM NUMBER GENERATOR
C     ALGORITHM AS-183, APPLIED STATISTICS VOL 31 NO 2, PP 188-190
C
      IX = 171 * MOD(IX, 177) - 2  * (IX /177)
      IY = 172 * MOD(IY, 176) - 35 * (IY/176)
      IZ = 170 * MOD(IZ, 178) - 63 * (IZ/178)
C
      IF(IX .LT. 0) IX = IX + 30269
      IF(IY .LT. 0) IY = IY + 30307
      IF(IZ .LT. 0) IZ = IZ + 30323
C
      RANDOM = AMOD(FLOAT(IX) / 30269.0 + FLOAT(IY) / 30307.0 +
     *              FLOAT(IZ) / 30323.0, 1.0)
      RETURN
      END
C*** fct.f
      SUBROUTINE BITREV(F,N)
      REAL F(N)
C***********************************************************************
C
C     Rearranges the data in the array F into bit-reverse order.
C
C     ARGUMENTS:
C       F   Array containing the data to be rearranged into 
C           bit-reversed order.
C       N   Length of the array F. N must be a power of two.
C
C     INTERNAL VARIABLES:
C       I
C       J
C       NV2
C       M
C       TEMP 
C
C***********************************************************************
      IF(N.LE.2) THEN
        RETURN
      ENDIF
      NV2 = N/2
      J = 1
      DO 20 I = 1, N
        IF (I .LT. J) THEN
          TEMP = F(J)
          F(J) = F(I)
          F(I) = TEMP
        ENDIF
        M = NV2
10      IF (J .LE. M) GOTO 20
          J = J - M
          M = (M + 1) / 2
        GOTO 10
20      J = J + M
      RETURN
      END


      SUBROUTINE SCRAMB(F,N)
      REAL F(N)
C***********************************************************************
C
C     Scrambles the data in the array F into ODD-EVEN order, i.e.
C     with the odd-indexed elements in the first half of the array
C     (in natural order), and the even elements in the second half
C     (in reverse order).
C
C     ARGUMENTS:
C       F   Array containing the data to be rearranged into ODD-EVEN
C           order.
C       N   Length of the array F. N must be a power of two.
C
C     INTERNAL VARIABLES:
C       I       Loop counter for swaps in top half of array.
C       II1     Index to first array element in swap.
C       II2     Index to second array element in swap.
C       NV2     Set equal to N/2.
C       NV4     Set equal to N/4.
C       TEMP    Temporary variable used while swapping array elements.
C
C***********************************************************************
      NV2 = N/2
      NV4 = N/4
      CALL BITREV(F,N)
      CALL BITREV(F,NV2)
      CALL BITREV(F(NV2+1),NV2)
C
      II1 = N
      II2 = NV2 + 1
      DO 30 I = 1,NV4
        TEMP = F(II1)
        F(II1) = F(II2)
        F(II2) = TEMP
        II1 = II1-1
        II2 = II2+1
30    CONTINUE
      RETURN
      END


      SUBROUTINE USCRAM(F,N)
      REAL F(N)
C***********************************************************************
C
C     Unscrambles the data in array F from ODD-EVEN order to natural
C     order, i.e. preforms the inverse of the operation performed by
C     subroutine SCRAMB.
C
C     ARGUMENTS:
C       F   Array containing the data to be unscrambled.
C       N   Length of the array F. N must be a power of two.
C
C     INTERNAL VARIABLES:  As for subroutine SCRAMB above.
C
C***********************************************************************
      NV2 = N/2
      NV4 = N/4
      II1 = N
      II2 = NV2+1
      DO 40 I = 1,NV4
        TEMP = F(II1)
        F(II1) = F(II2)
        F(II2) = TEMP
        II1 = II1-1
        II2 = II2+1
40    CONTINUE
      CALL BITREV(F,NV2)
      CALL BITREV(F(NV2+1),NV2)
      CALL BITREV(F,N)
      RETURN
      END


      SUBROUTINE FCT(F,C,N,IFAULT)
      REAL F(N), C(N)
C***********************************************************************
C
C     FAST DISCRETE COSINE TRANSFORM
C
C     ARGUMENTS:
C       F      Array containing the data to be transformed. On exit
C              it contains the transformed sequence.
C       C      Array containing cosine coefficients as previously 
C              calculated by a call to INIFCT. When this is the first
C              call to FCT or IFCT for the current value of N, this
C              array will be initialised by a call to INIFCT.
C
C       N      Length of the array F. N must be a power of two.
C       IFAULT On exit:
C                =0,  if there are no faults.
C                =1,  if N <= 1.
C                =2,  if N > 2**MAXPOW. (See subroutine INIFCT below).
C                =3,  if 2 <= N <= 2**MAXPOW but N is not a power of two.
C
C     INTERNAL VARIABLES:
C       CFAC    Cosine multiplicative factor used in butterfly.
C       IBASE   Index of base of butterfly.
C       IBUTFY  Butterfly counter.
C       I       Loop counter used when scaling the DCT coefficients.
C       II1     Index to first element of butterfly.
C       II2     Index to second element of butterfly.
C       IGROUP  Loop counter for groups of butterflies.
C       INCR    Index increment to move from one group of butterflies to
C               the next.
C       INDEX   Index to array elements during summations.
C       ISTAGE  Counter for stages during butterfly or summation 
C               processing.
C       ISTEP   Counter for steps within a thread during summation
C               processing.
C       ISTPSZ  Size of step within a thread during summation processing.
C       IWNGSP  Width (wingspan) od butterfly.
C       NGRPS   Number of groups of butterflies in current stage.
C       NSTEPS  Number of steps in currect thread during summation 
C               processing.
C       NTHRDS  Number of summation threads in current summation stage.
C       NV2     Set to N/2.
C       RT2INV  The constant 1/SQRT(2).
C       TEMP    Temporary variable used during butterfly processing.
C       TWO     The constane 2.0.
C       TWOVN   Set to 2.0/N.
C
C     COMMON DATA:
C       As for subroutine INIFCT below.
C
C***********************************************************************
      PARAMETER (TWO=2.0, RT2INV=0.70710 67811 8655)
      REAL TEMP,CFAC,TWOVN
      COMMON /FCTLEN/ LENGTH,IPOW
      SAVE /FCTLEN/
C
C     CHECK FOR VALID TRANSFORM SIZE
C
      IFAULT = 0
      IF((N.NE.LENGTH).OR.(N.LE.1)) CALL INIFCT(C,N,IFAULT)
      IF(IFAULT.NE.0) RETURN
C
      NV2 = N/2
C
C     SCRAMBLE THE DATA IN F INTO ODD-EVEN ORDERING
C
      CALL SCRAMB(F,N)
C
C     DO THE BUTTERFLIES
C
      NGRPS = 1
      IWNGSP = NV2
      INCR = N
      DO 70 ISTAGE = IPOW, 1, -1
        DO 60 IBUTFY = 1, IWNGSP
          CFAC = C(IWNGSP + IBUTFY)
          IBASE = 0
          DO 50 IGROUP = 1, NGRPS
            II1 = IBASE + IBUTFY
            II2 = II1 + IWNGSP
            TEMP = F(II2)
            F(II2) = CFAC * (F(II1) - TEMP)
            F(II1) = F(II1) + TEMP
            IBASE = IBASE + INCR
50        CONTINUE
60      CONTINUE
        INCR = INCR / 2
        IWNGSP = IWNGSP / 2
        NGRPS = NGRPS + NGRPS
70    CONTINUE
C
C     BIT-REVERSE ORDER ARRAY F
C
      CALL BITREV(F,N)
C
C     DO THE SUMS
C
      NTHRDS = N/4
      ISTPSZ = NV2
      NSTEPS   = 2
      DO 100 ISTAGE = IPOW-1, 1, -1
        DO 90 THREAD = 1, NTHRDS
          INDEX = NTHRDS + THREAD
          DO 80 ISTEP = 1, NSTEPS-1
            F(INDEX) = F(INDEX) + F(INDEX + ISTPSZ)
            INDEX = INDEX + ISTPSZ
80        CONTINUE
90      CONTINUE      
        NSTEPS = NSTEPS + NSTEPS
        NTHRDS = NTHRDS / 2
        ISTPSZ = ISTPSZ / 2
100   CONTINUE
C
C     SCALE THE RESULT
C
      TWOVN = TWO / (FLOAT(N))
      DO 110 I = 1, N
110     F(I) = F(I) * TWOVN
      F(1) = F(1) * RT2INV
      RETURN
      END


      SUBROUTINE IFCT(F,C,N,IFAULT)
      REAL F(N), C(N)
C***********************************************************************
C
C     FAST INVERSE DISCRETE COSINE TRANSFORM
C
C     ARGUMENTS:
C       F      Array containing the data to be inverse transformed.
C              On exit, it contains the inverse transformed data.
C       C,N,IFAULT  As for subroutine FCT above.
C
C     INTERNAL VARIABLES:
C       As for subroutine FCT above. 
C
C     COMMON DATA:
C       As for subroutine INIFCT below.
C***********************************************************************
      PARAMETER(RT2INV=0.70710 67811 8655)
      REAL TEMP,CFAC
      COMMON /FCTLEN/ LENGTH,IPOW
      SAVE /FCTLEN/
C
C     CHECK TRANSFORM LENGTH AND INITIALISE IF NECESSARY
C
      IFAULT = 0
      IF((N.NE.LENGTH).OR.(N.LE.1)) CALL INIFCT(C,N,IFAULT)
      IF(IFAULT.NE.0) RETURN
C     
      NV2 = N/2
C
      F(1) = F(1) * RT2INV
C
C     DO THE SUMS
C
      NTHRDS = 1
      ISTPSZ = 2
      NSTEPS   = NV2
      DO 140 ISTAGE = 1, IPOW-1
        DO 130 THREAD = 1, NTHRDS
          INDEX = N - THREAD + 1
          DO 120 ISTEP = 1, NSTEPS-1
            F(INDEX) = F(INDEX) + F(INDEX - ISTPSZ)
            INDEX = INDEX - ISTPSZ
120       CONTINUE
130     CONTINUE      
        NSTEPS = NSTEPS / 2
        NTHRDS = NTHRDS + NTHRDS
        ISTPSZ = ISTPSZ + ISTPSZ
140   CONTINUE
C
C     BIT-REVERSE ORDER ARRAY F
C
      CALL BITREV(F,N)
C
C     DO THE BUTTERFLIES
C
      NGRPS = NV2
      IWNGSP = 1
      INCR = 2
      DO 170 ISTAGE = 1, IPOW
        DO 160 IBUTFY = 1, IWNGSP
          CFAC = C(IWNGSP + IBUTFY)
          IBASE = 0
          DO 150 IGROUP = 1, NGRPS
            II1 = IBASE + IBUTFY
            II2 = II1 + IWNGSP
            TEMP = CFAC * F(II2)
            F(II2) = F(II1) - TEMP
            F(II1) = F(II1) + TEMP
            IBASE = IBASE + INCR
150       CONTINUE
160     CONTINUE
        INCR = INCR + INCR
        IWNGSP = IWNGSP + IWNGSP
        NGRPS = NGRPS/2
170   CONTINUE
C
C     UNSCRAMBLE FROM ODD-EVEN ORDERING TO NATURAL ORDERING
C
      CALL USCRAM(F,N)
C
      RETURN
      END


      BLOCK DATA
      COMMON /FCTLEN/ LENGTH,IPOW
      DATA LENGTH/0/,IPOW/0/
      END


      SUBROUTINE INIFCT(C,N,IFAULT)
      REAL C(N)
      PARAMETER(PI=3.14159 26535 89793 23846, ONE=1.0, TWO=2.0, 
     *  MAXPOW=20)
      REAL ZCOS
      COMMON /FCTLEN/ LENGTH,IPOW
      SAVE /FCTLEN/
      ZCOS(A) = COS(A)
C***********************************************************************
C
C     INITIALIZES THE ARRAY C OF COSINE COEFFICIENTS AS REQUIRED FOR 
C     CALLS TO SUBROUTINES FCT AND IFCT. ALSO CHECKS THE VALIDITY OF
C     THE TRANSFORM SIZE N. CALLED WHENEVER SUBROUTINES FCT OR IFCT
C     RECEIVE A NEW TRANSFORM SIZE. NEVER CALLED EXPLICITLY BY THE
C     USER.
C
C     ARGUMENTS:
C       C      Array to hold the cosine coefficients.
C       N      Length of the array C, i.e. size of transform desired.
C       IFAULT As described in subroutine FCT above.
C
C     INTERNAL VARIABLES:
C       I       Loop counter to index array elements while loading top
C               half of C array, and while taking cosines of the array
C               values.
C       IFAC    Multiplication factor used while filling bottom half
C               of C array.
C       IGROUP  Loop counter for groups of C array coefficients in
C               bottom half.
C       II      Temporary variable to hold powers of two while the
C               transform length is being checked.
C       ITEM    Loop counter to count items within a group of C array
C               coefficients.
C       K       Loop counter counting successive powers of two during
C               transform length checking.
C       MAXPOW  Parameter which determines maximum transform length
C               permitted. Maximum length = 2**MAXPOW.
C       NITEMS  Number of items within current group of C array
C               coefficients.
C       NV2     Set to N/2.
C       ONE     The constant 1.0.
C       PI      The constant pi = 3.1415926.
C       TWO     The constant 2.0.
C
C     COMMON DATA:
C       FCTLEN  Consists of:
C            LENGTH: The transform length for which the array C of 
C              cosine coefficients is currently set up. Used by FCT and
C              IFCT to determine whether transform length has changed.
C            IPOW: The base-2 logarithm of LENGTH. Used in FCT and IFCT
C              to determine number of stages of butterflies or 
C              summations. 
C
C***********************************************************************
C
C     CHECK FOR VALID TRANSFORM SIZE
C
      PRINT *, 'INIFCT -- N=',N
      IFAULT = 0
      IF(N.LE.1) THEN
        IFAULT = 1
        RETURN
      ENDIF
      II = 1
      DO 180 K = 1, MAXPOW
        II = II + II
        IF (II .EQ. N) GOTO 190
        IF (II .GT. N) THEN
          IFAULT = 3
          RETURN
        ENDIF
180   CONTINUE
      IFAULT = 2
      RETURN
C      
C     IF WE REACH THIS POINT, TRANSFORM LENGTH IS VALID.
C
190   IPOW = K
      LENGTH = N
      NV2 = N/2
C
C     PUT VALUES INTO TOP HALF OF C ARRAY
C
      DO 200 I = 1, NV2
200     C(NV2 + I) = FLOAT(4*I-3)
C
C     COPY SCALED VALUES FROM TOP HALF OF C ARRAY TO BOTTOM HALF
C
      NITEMS = 1
      IFAC = NV2
      DO 220 IGROUP = 1, IPOW - 1
        DO 210 ITEM = 1, NITEMS        
210       C(NITEMS + ITEM) = FLOAT(IFAC) * C(NV2 + ITEM)
        NITEMS = NITEMS + NITEMS
        IFAC = IFAC / 2
220   CONTINUE
C
C     TAKE COSINE OF EACH ELEMENT OF C ARRAY
C
      DO 230 I = 2, N
230     C(I) = ONE / (TWO * ZCOS ( C(I) * PI / FLOAT(N+N) ))
C
      RETURN
      END
C*** fctd.f
      SUBROUTINE BITREV(F,N)
      DOUBLE PRECISION F(N)
C***********************************************************************
C
C     Rearranges the data in the array F into bit-reverse order.
C
C     ARGUMENTS:
C       F   Array containing the data to be rearranged into 
C           bit-reversed order.
C       N   Length of the array F. N must be a power of two.
C
C     INTERNAL VARIABLES:
C       I
C       J
C       NV2
C       M
C       TEMP 
C
C***********************************************************************
      DOUBLE PRECISION TEMP
C
      IF(N.LE.2) THEN
        RETURN
      ENDIF
      NV2 = N/2
      J = 1
      DO 20 I = 1, N
        IF (I .LT. J) THEN
          TEMP = F(J)
          F(J) = F(I)
          F(I) = TEMP
        ENDIF
        M = NV2
10      IF (J .LE. M) GOTO 20
          J = J - M
          M = (M + 1) / 2
        GOTO 10
20      J = J + M
      RETURN
      END


      SUBROUTINE SCRAMB(F,N)
      DOUBLE PRECISION F(N)
C***********************************************************************
C
C     Scrambles the data in the array F into ODD-EVEN order, i.e.
C     with the odd-indexed elements in the first half of the array
C     (in natural order), and the even elements in the second half
C     (in reverse order).
C
C     ARGUMENTS:
C       F   Array containing the data to be rearranged into ODD-EVEN
C           order.
C       N   Length of the array F. N must be a power of two.
C
C     INTERNAL VARIABLES:
C       I       Loop counter for swaps in top half of array.
C       II1     Index to first array element in swap.
C       II2     Index to second array element in swap.
C       NV2     Set equal to N/2.
C       NV4     Set equal to N/4.
C       TEMP    Temporary variable used while swapping array elements.
C
C***********************************************************************
      DOUBLE PRECISION TEMP
C
      NV2 = N/2
      NV4 = N/4
      CALL BITREV(F,N)
      CALL BITREV(F,NV2)
      CALL BITREV(F(NV2+1),NV2)
C
      II1 = N
      II2 = NV2 + 1
      DO 30 I = 1,NV4
        TEMP = F(II1)
        F(II1) = F(II2)
        F(II2) = TEMP
        II1 = II1-1
        II2 = II2+1
30    CONTINUE
      RETURN
      END


      SUBROUTINE USCRAM(F,N)
      DOUBLE PRECISION F(N)
C***********************************************************************
C
C     Unscrambles the data in array F from ODD-EVEN order to natural
C     order, i.e. preforms the inverse of the operation performed by
C     subroutine SCRAMB.
C
C     ARGUMENTS:
C       F   Array containing the data to be unscrambled.
C       N   Length of the array F. N must be a power of two.
C
C     INTERNAL VARIABLES:  As for subroutine SCRAMB above.
C
C***********************************************************************
      DOUBLE PRECISION TEMP
C
      NV2 = N/2
      NV4 = N/4
      II1 = N
      II2 = NV2+1
      DO 40 I = 1,NV4
        TEMP = F(II1)
        F(II1) = F(II2)
        F(II2) = TEMP
        II1 = II1-1
        II2 = II2+1
40    CONTINUE
      CALL BITREV(F,NV2)
      CALL BITREV(F(NV2+1),NV2)
      CALL BITREV(F,N)
      RETURN
      END


      SUBROUTINE FCT(F,C,N,IFAULT)
      DOUBLE PRECISION F(N), C(N)
C***********************************************************************
C
C     FAST DISCRETE COSINE TRANSFORM
C
C     ARGUMENTS:
C       F      Array containing the data to be transformed. On exit
C              it contains the transformed sequence.
C       C      Array containing cosine coefficients as previously 
C              calculated by a call to INIFCT. When this is the first
C              call to FCT or IFCT for the current value of N, this
C              array will be initialised by a call to INIFCT.
C
C       N      Length of the array F. N must be a power of two.
C       IFAULT On exit:
C                =0,  if there are no faults.
C                =1,  if N <= 1.
C                =2,  if N > 2**MAXPOW. (See subroutine INIFCT below).
C                =3,  if 2 <= N <= 2**MAXPOW but N is not a power of two.
C
C     INTERNAL VARIABLES:
C       CFAC    Cosine multiplicative factor used in butterfly.
C       IBASE   Index of base of butterfly.
C       IBUTFY  Butterfly counter.
C       I       Loop counter used when scaling the DCT coefficients.
C       II1     Index to first element of butterfly.
C       II2     Index to second element of butterfly.
C       IGROUP  Loop counter for groups of butterflies.
C       INCR    Index increment to move from one group of butterflies to
C               the next.
C       INDEX   Index to array elements during summations.
C       ISTAGE  Counter for stages during butterfly or summation 
C               processing.
C       ISTEP   Counter for steps within a thread during summation
C               processing.
C       ISTPSZ  Size of step within a thread during summation processing.
C       IWNGSP  Width (wingspan) od butterfly.
C       NGRPS   Number of groups of butterflies in current stage.
C       NSTEPS  Number of steps in currect thread during summation 
C               processing.
C       NTHRDS  Number of summation threads in current summation stage.
C       NV2     Set to N/2.
C       RT2INV  The constant 1/SQRT(2).
C       TEMP    Temporary variable used during butterfly processing.
C       TWO     The constane 2.0.
C       TWOVN   Set to 2.0/N.
C
C     COMMON DATA:
C       As for subroutine INIFCT below.
C
C***********************************************************************
      DOUBLE PRECISION TWO, RT2INV
      PARAMETER (TWO=2.0D0, RT2INV=0.70710 67811 8655D0)
      DOUBLE PRECISION TEMP,CFAC,TWOVN
      COMMON /FCTLEN/ LENGTH,IPOW
      SAVE /FCTLEN/
C
C     CHECK FOR VALID TRANSFORM SIZE
C
      IFAULT = 0
      IF((N.NE.LENGTH).OR.(N.LE.1)) CALL INIFCT(C,N,IFAULT)
      IF(IFAULT.NE.0) RETURN
C
      NV2 = N/2
C
C     SCRAMBLE THE DATA IN F INTO ODD-EVEN ORDERING
C
      CALL SCRAMB(F,N)
C
C     DO THE BUTTERFLIES
C
      NGRPS = 1
      IWNGSP = NV2
      INCR = N
      DO 70 ISTAGE = IPOW, 1, -1
        DO 60 IBUTFY = 1, IWNGSP
          CFAC = C(IWNGSP + IBUTFY)
          IBASE = 0
          DO 50 IGROUP = 1, NGRPS
            II1 = IBASE + IBUTFY
            II2 = II1 + IWNGSP
            TEMP = F(II2)
            F(II2) = CFAC * (F(II1) - TEMP)
            F(II1) = F(II1) + TEMP
            IBASE = IBASE + INCR
50        CONTINUE
60      CONTINUE
        INCR = INCR / 2
        IWNGSP = IWNGSP / 2
        NGRPS = NGRPS + NGRPS
70    CONTINUE
C
C     BIT-REVERSE ORDER ARRAY F
C
      CALL BITREV(F,N)
C
C     DO THE SUMS
C
      NTHRDS = N/4
      ISTPSZ = NV2
      NSTEPS   = 2
      DO 100 ISTAGE = IPOW-1, 1, -1
        DO 90 THREAD = 1, NTHRDS
          INDEX = NTHRDS + THREAD
          DO 80 ISTEP = 1, NSTEPS-1
            F(INDEX) = F(INDEX) + F(INDEX + ISTPSZ)
            INDEX = INDEX + ISTPSZ
80        CONTINUE
90      CONTINUE      
        NSTEPS = NSTEPS + NSTEPS
        NTHRDS = NTHRDS / 2
        ISTPSZ = ISTPSZ / 2
100   CONTINUE
C
C     SCALE THE RESULT
C
      TWOVN = TWO / (DBLE(N))
      DO 110 I = 1, N
110     F(I) = F(I) * TWOVN
      F(1) = F(1) * RT2INV
      RETURN
      END


      SUBROUTINE IFCT(F,C,N,IFAULT)
      DOUBLE PRECISION F(N), C(N)
C***********************************************************************
C
C     FAST INVERSE DISCRETE COSINE TRANSFORM
C
C     ARGUMENTS:
C       F      Array containing the data to be inverse transformed.
C              On exit, it contains the inverse transformed data.
C       C,N,IFAULT  As for subroutine FCT above.
C
C     INTERNAL VARIABLES:
C       As for subroutine FCT above. 
C
C     COMMON DATA:
C       As for subroutine INIFCT below.
C***********************************************************************
      DOUBLE PRECISION RT2INV
      PARAMETER(RT2INV=0.70710 67811 8655D0)
      DOUBLE PRECISION TEMP,CFAC
      COMMON /FCTLEN/ LENGTH,IPOW
      SAVE /FCTLEN/
C
C     CHECK TRANSFORM LENGTH AND INITIALISE IF NECESSARY
C
      IFAULT = 0
      IF((N.NE.LENGTH).OR.(N.LE.1)) CALL INIFCT(C,N,IFAULT)
      IF(IFAULT.NE.0) RETURN
C     
      NV2 = N/2
C
      F(1) = F(1) * RT2INV
C
C     DO THE SUMS
C
      NTHRDS = 1
      ISTPSZ = 2
      NSTEPS   = NV2
      DO 140 ISTAGE = 1, IPOW-1
        DO 130 THREAD = 1, NTHRDS
          INDEX = N - THREAD + 1
          DO 120 ISTEP = 1, NSTEPS-1
            F(INDEX) = F(INDEX) + F(INDEX - ISTPSZ)
            INDEX = INDEX - ISTPSZ
120       CONTINUE
130     CONTINUE      
        NSTEPS = NSTEPS / 2
        NTHRDS = NTHRDS + NTHRDS
        ISTPSZ = ISTPSZ + ISTPSZ
140   CONTINUE
C
C     BIT-REVERSE ORDER ARRAY F
C
      CALL BITREV(F,N)
C
C     DO THE BUTTERFLIES
C
      NGRPS = NV2
      IWNGSP = 1
      INCR = 2
      DO 170 ISTAGE = 1, IPOW
        DO 160 IBUTFY = 1, IWNGSP
          CFAC = C(IWNGSP + IBUTFY)
          IBASE = 0
          DO 150 IGROUP = 1, NGRPS
            II1 = IBASE + IBUTFY
            II2 = II1 + IWNGSP
            TEMP = CFAC * F(II2)
            F(II2) = F(II1) - TEMP
            F(II1) = F(II1) + TEMP
            IBASE = IBASE + INCR
150       CONTINUE
160     CONTINUE
        INCR = INCR + INCR
        IWNGSP = IWNGSP + IWNGSP
        NGRPS = NGRPS/2
170   CONTINUE
C
C     UNSCRAMBLE FROM ODD-EVEN ORDERING TO NATURAL ORDERING
C
      CALL USCRAM(F,N)
C
      RETURN
      END


      BLOCK DATA
      COMMON /FCTLEN/ LENGTH,IPOW
      DATA LENGTH/0/,IPOW/0/
      END


      SUBROUTINE INIFCT(C,N,IFAULT)
      DOUBLE PRECISION C(N)
      DOUBLE PRECISION PI, ONE, TWO
      PARAMETER(PI=3.14159 26535 89793 23846D0, ONE=1.0D0, TWO=2.0D0,
     *  MAXPOW=20)
      DOUBLE PRECISION ZCOS, A
      COMMON /FCTLEN/ LENGTH,IPOW
      SAVE /FCTLEN/
      ZCOS(A) = DCOS(A)
C***********************************************************************
C
C     INITIALIZES THE ARRAY C OF COSINE COEFFICIENTS AS REQUIRED FOR 
C     CALLS TO SUBROUTINES FCT AND IFCT. ALSO CHECKS THE VALIDITY OF
C     THE TRANSFORM SIZE N. CALLED WHENEVER SUBROUTINES FCT OR IFCT
C     RECEIVE A NEW TRANSFORM SIZE. NEVER CALLED EXPLICITLY BY THE
C     USER.
C
C     ARGUMENTS:
C       C      Array to hold the cosine coefficients.
C       N      Length of the array C, i.e. size of transform desired.
C       IFAULT As described in subroutine FCT above.
C
C     INTERNAL VARIABLES:
C       I       Loop counter to index array elements while loading top
C               half of C array, and while taking cosines of the array
C               values.
C       IFAC    Multiplication factor used while filling bottom half
C               of C array.
C       IGROUP  Loop counter for groups of C array coefficients in
C               bottom half.
C       II      Temporary variable to hold powers of two while the
C               transform length is being checked.
C       ITEM    Loop counter to count items within a group of C array
C               coefficients.
C       K       Loop counter counting successive powers of two during
C               transform length checking.
C       MAXPOW  Parameter which determines maximum transform length
C               permitted. Maximum length = 2**MAXPOW.
C       NITEMS  Number of items within current group of C array
C               coefficients.
C       NV2     Set to N/2.
C       ONE     The constant 1.0.
C       PI      The constant pi = 3.1415926.
C       TWO     The constant 2.0.
C
C     COMMON DATA:
C       FCTLEN  Consists of:
C            LENGTH: The transform length for which the array C of 
C              cosine coefficients is currently set up. Used by FCT and
C              IFCT to determine whether transform length has changed.
C            IPOW: The base-2 logarithm of LENGTH. Used in FCT and IFCT
C              to determine number of stages of butterflies or 
C              summations. 
C
C***********************************************************************
C
C     CHECK FOR VALID TRANSFORM SIZE
C
      PRINT *, 'INIFCT -- N=',N
      IFAULT = 0
      IF(N.LE.1) THEN
        IFAULT = 1
        RETURN
      ENDIF
      II = 1
      DO 180 K = 1, MAXPOW
        II = II + II
        IF (II .EQ. N) GOTO 190
        IF (II .GT. N) THEN
          IFAULT = 3
          RETURN
        ENDIF
180   CONTINUE
      IFAULT = 2
      RETURN
C      
C     IF WE REACH THIS POINT, TRANSFORM LENGTH IS VALID.
C
190   IPOW = K
      LENGTH = N
      NV2 = N/2
C
C     PUT VALUES INTO TOP HALF OF C ARRAY
C
      DO 200 I = 1, NV2
200     C(NV2 + I) = DBLE(4*I-3)
C
C     COPY SCALED VALUES FROM TOP HALF OF C ARRAY TO BOTTOM HALF
C
      NITEMS = 1
      IFAC = NV2
      DO 220 IGROUP = 1, IPOW - 1
        DO 210 ITEM = 1, NITEMS        
210       C(NITEMS + ITEM) = DBLE(IFAC) * C(NV2 + ITEM)
        NITEMS = NITEMS + NITEMS
        IFAC = IFAC / 2
220   CONTINUE
C
C     TAKE COSINE OF EACH ELEMENT OF C ARRAY
C
      DO 230 I = 2, N
230     C(I) = ONE / (TWO * ZCOS ( C(I) * PI / DBLE(N+N) ))
C
      RETURN
      END
