SUBROUTINE POLYFIT( STATUS )
*+
*  Name:
*     POLYFIT

*  Purpose:
*     Fits 1D polynomials to nD data. Can subtract fit from data too.

*  Language:
*     Starlink Fortran

*  Type of Module:
*     ASTERIX task

*  Invocation:
*     CALL POLYFIT( STATUS )

*  Arguments:
*     STATUS = INTEGER (Given and Returned)
*        The global status.

*  Description:
*     {routine_description}

*  Usage:
*     polyfit {parameter_usage}

*  Environment Parameters:
*     INP = CHAR (read)
*        Dataset to be fitted
*     OVER = LOGICAL (read)
*        Overwrite input with fit (Y), or create new output file      
*     OUT = CHAR (read)
*        Output dataset
*     FIT = LOGICAL (read)
*        Write the fit, or the input data minus the fit?
*     SUPER = LOGICAL (read)
*        Write the fit coefficients to the GCB of the output?      
*     DEGREE = INTEGER (read)
*        Preferred degree of polynomial fit      

*  Examples:
*     {routine_example_text}
*        {routine_example_description}

*  Pitfalls:
*     {pitfall_description}...

*  Notes:
*     {routine_notes}...

*  Prior Requirements:
*     {routine_prior_requirements}...

*  Side Effects:
*     {routine_side_effects}...

*  Algorithm:
*     Calculates a 1 dimensional polynomial fit to the data.
*     If the data is greater than 1D, then a series of 1D fits are performed.
*     The output dataset can either consist of:
*         input data - fit; or
*         fit, depending upon the value of the parameter POLY.
*     The input dataset may be over written

*  Accuracy:
*     {routine_accuracy}

*  Timing:
*     {routine_timing}

*  Implementation Status:
*     {routine_implementation_status}

*  External Routines Used:
*     {name_of_facility_or_package}:
*        {routine_used}...

*  Implementation Deficiencies:
*     {routine_deficiencies}...

*  References:
*     {task_references}...

*  Keywords:
*     polyfit, usage:public

*  Copyright:
*     Copyright (C) University of Birmingham, 1995

*  Authors:
*     TJP: Trevor Ponman (University of Birmingham)
*     DJA: David J. Allan (Jet-X, University of Birmingham)
*     {enter_new_authors_here}

*  History:
*     29 Jun 1984 V0.6-0 (TJP):
*        Original version.
*     12 Dec 1984 V0.6-1 (TJP):
*        Mapped for UPDATE instead of WRITE 
*     30 Mar 1987 V0.6-2 (PLA):
*        Code tidied up, and brought to STARLINK standard
*      2 Sep 1987 V1.0-2 (PLA):
*        Operates on primitive input object
*     16 Sep 1988 V1.0-3 (PLA):
*        OVERWRITE option, & now uses MATH_POLY subroutine 
*      6 Oct 1988 V1.0-4 (PLA):
*        Displays correct values of the coefficients
*     14 Nov 1988 V1.0-5 (PLA):
*        Handles nD datasets by performing repeated 1D fits. MATH_POLY code
*        improved and included within this file because normalization of AXIS
*        values done out side of altered MATH_POLY (here POLYFIT_DOIT).
*      8 Oct 1992 V1.7-0 (DJA):
*        Changes for alterations in compiler 3 years ago. This
*        program is either bug free or never used 
*     24 Nov 1994 V1.8-0 (DJA):
*        Now use USI for user interface 
*     29 Nov 1995 V2.0-0 (RJV):
*        Writes fit data into GCB
*     11 Dec 1995 V2.0-1 (DJA):
*        ADI port
*     {enter_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
      
*  Type Definitions:
      IMPLICIT NONE              ! No implicit typing

*  Global Constants:
      INCLUDE 'SAE_PAR'          ! Standard SAE constants
      INCLUDE 'ADI_PAR'

*  Status:
      INTEGER			STATUS             	! Global status

*  Local Constants:
      CHARACTER*19              Fmt             ! Format for output of coefficients
        PARAMETER               ( Fmt = '(1X,G15.6,A,I2,A)' )

      CHARACTER*30		VERSION
        PARAMETER		( VERSION = 'POLYFIT Version V2.0-1' )

*  Local Variables:
      CHARACTER*80              HTXT			! History text
      CHARACTER*12              NAME
      
      REAL                      PARAM(12)
      REAL			SPARR(2)

      INTEGER                AXPTR           ! Pointers to mapped axis values
      INTEGER                BLEN            ! No. of fits to be performed
      INTEGER                DPTR            ! Pointer to input data array
      INTEGER                I, N            ! Dummy variables for loops.
      INTEGER			IFID			! Input dataset id
      INTEGER			IFILES			! Input file info
      INTEGER                LDIMS(ADI__MXDIM) ! Size of each dimension
      INTEGER                NBAD            ! No.of bad quality data
      INTEGER                NBPTR           ! Pointer to number of bad points array
      INTEGER                NDAT            ! Total number of data points
      INTEGER                NDEG            ! Degree of polynomial fitted
      INTEGER                NDIM            ! Dimensionality of data
      INTEGER			OFID			! Input dataset id
      INTEGER                	QPTR            	! Pointer to data quality
      INTEGER                   VPTR                    ! Pointer to data variances
      INTEGER                WTPTR           ! Data weights (=1/variance**2)

      LOGICAL                OK              ! Are various components OK to be used
      LOGICAL                OVERWRITE       ! Over write input object?
      LOGICAL                POLY            ! Selects DTREND or POLYFIT function.
      LOGICAL                QUALOK          ! Is QUALITY present & ok ?
      LOGICAL                VAROK           ! Is VARIANCE present & ok ?
      LOGICAL                WFIT            ! Perform weighted fit?
      LOGICAL                SUPER           ! Superimpose fit
*.

*  Check inherited global status.
      IF ( STATUS .NE. SAI__OK ) RETURN

*  Version id
      CALL MSG_PRNT( VERSION )

*  Initialise ASTERIX
      CALL AST_INIT()

*  Ask if detrending or polynomial fitting required:
      CALL USI_GET0L( 'FIT', POLY, STATUS )
      IF ( STATUS .NE. SAI__OK ) GOTO 99
      IF ( POLY ) THEN
        CALL USI_GET0L( 'DTREND', POLY, STATUS )
        POLY = .NOT. POLY
      END IF

*  Find out if are overwritting input file
      CALL USI_GET0L( 'OVER', OVERWRITE, STATUS )
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  See if fit data to be written to GCB for superimposing fit
      CALL USI_GET0L( 'SUPER', SUPER, STATUS )

*  Open input
      IF ( OVERWRITE ) THEN
        CALL MSG_PRNT( 'WARNING: Overwriting input object' )
        CALL USI_ASSOC( 'INP', 'BinDS|Array', 'UPDATE', IFID, STATUS )
        OFID = IFID
      ELSE
        IF ( SUPER ) THEN
          CALL USI_ASSOC( 'INP', 'BinDS|Array', 'UPDATE', IFID,
     :                    STATUS )
        ELSE
          CALL USI_ASSOC( 'INP', 'BinDS|Array', 'READ', IFID,
     :                    STATUS )
        ENDIF
        CALL USI_CLONE( 'INP', 'OUT', 'BinDS|Array', OFID, STATUS )
      END IF
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  Check data
      CALL BDI_CHK( IFID, 'Data', OK, STATUS )
      CALL BDI_GETSHP( IFID, ADI__MXDIM, LDIMS, NDIM, STATUS )
      IF ( .NOT. OK ) THEN
        STATUS = SAI__ERROR
        CALL ERR_REP( ' ', '! Invalid data', STATUS )
      END IF
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  Pad dimensions to 7-D
      CALL AR7_PAD( NDIM, LDIMS, STATUS )
      
*  Look for independent variable - if present & correct then map it
      CALL BDI_AXCHK( IFID, 1, 'Data', OK, STATUS )
      IF ( .NOT. OK ) THEN

*      Set up temporary data object containing equally spaced values
        CALL MSG_PRNT ('WARNING: Axis(1) data is invalid'/
     :                        /' - proceeding assuming regular spacing')

        SPARR(1) = 0.0
        SPARR(1) = 1.0
        DO I = 1, NDIM
          CALL BDI_AXPUT1R( OFID, I, 'SpacedData', 2, SPARR, STATUS )
        END DO

      END IF
      CALL BDI_AXMAPR( IFID, 1, 'Data', 'READ', AXPTR, STATUS )
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  Map in data array
      CALL BDI_MAPR( OFID, 'Data', 'UPDATE', DPTR, STATUS )

*  Map data variance if present & correct
      CALL BDI_CHK( IFID, 'Variance', VAROK, STATUS )
      IF ( VAROK ) THEN
        CALL BDI_MAPR( IFID, 'Variance', 'READ', VPTR, STATUS )
      END IF

*  Check data quality - exclude any bad points from fit.
      CALL BDI_CHK( IFID, 'Quality', QUALOK, STATUS )
      IF ( QUALOK ) THEN
        CALL BDI_MAPL( IFID, 'LogicalQuality', 'READ', QPTR, STATUS )
      END IF
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  Inform user about number of data points
      CALL ARR_SUMDIM( NDIM, LDIMS, NDAT )
      CALL MSG_SETI( 'NDAT', NDAT )
      CALL MSG_PRNT( '^NDAT points entered' )
      IF ( NDIM .GT. 1 ) THEN
        CALL MSG_SETI( 'NDAT', LDIMS(1) )
        CALL MSG_PRNT( '^NDAT points in each 1 dimensional fit' )
      END IF

*  User input.
      CALL USI_GET0I( 'DEGREE', NDEG, STATUS )
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  NDEG must lie between 0 and 10
      IF ( NDEG .GT. 10 ) THEN
        NDEG = 10
        CALL MSG_PRNT( 'WARNING: Will only calculate up to '//
     :                              '10th order polynomial.' )
      ELSE IF ( NDEG .LT. 0 ) THEN
        NDEG = 0
        CALL MSG_PRNT( 'WARNING: Minimum degree of polynomial is zero.')

      END IF

      BLEN = NDAT / LDIMS(1)
      WFIT = .FALSE.


      IF ( VAROK .OR. QUALOK ) THEN
        WFIT = .TRUE.

*    Create dynamic array to hold weights
        CALL DYN_MAPR( 1, NDAT, WTPTR, STATUS )

*    Create dynamic array to hold number of bad points
        CALL DYN_MAPI( 1, BLEN, NBPTR, STATUS )
        IF ( STATUS .NE. SAI__OK ) GOTO 99

*    Set up array of weights.
        CALL POLYFIT_WEIGHTS( VAROK, QUALOK, LDIMS(1), BLEN, %VAL(VPTR),
     :              %VAL(QPTR), %VAL(WTPTR), %VAL(NBPTR), NBAD, STATUS )

        IF (NBAD .GT. 0 ) THEN
          CALL MSG_SETI( 'BADPTS', NBAD )
          CALL MSG_PRNT( 'A total of ^BADPTS bad data points'/
     :                     /' will be excluded from the fit' )

          IF ( NBAD .EQ. NDAT ) THEN
            STATUS = SAI__ERROR
            CALL ERR_REP( ' ', 'FATAL ERROR: All data excluded',
     :                                                  STATUS )
          END IF

        END IF

*    Loop over NDIM-1 dimensions, performing 1 d fits
        CALL POLYFIT_LOOPWT (POLY, NDEG, NDIM, LDIMS(1), BLEN,
     :        %VAL(AXPTR), %VAL(NBPTR), %VAL(WTPTR), %VAL(DPTR),
     :                                           PARAM,  STATUS)

      ELSE

*      Loop over NDIM-1 dimensions, performing 1 d fits
        CALL POLYFIT_LOOPNOWT( POLY, NDEG, NDIM, LDIMS(1), BLEN,
     :                         %VAL(AXPTR), %VAL(DPTR),PARAM,STATUS)

      END IF
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  Write fit data into GCB
      IF ( SUPER ) THEN
        CALL GCB_LCONNECT( STATUS )
        CALL GCB_FLOAD( IFID, STATUS )
        CALL GCB_SETL( 'FUNC_FLAG', .TRUE., STATUS )
        CALL GCB_SETC( 'FUNC_TYPE', 'POLY', STATUS )
        NAME = 'FUNC_PAR'
        DO I = 1, MIN(6,NDEG+1)
          WRITE(NAME(9:9),'(I1)') I
          CALL GCB_SETR(NAME,PARAM(I),STATUS)
        ENDDO
        DO I = MIN(6,NDEG+1)+1,6
          WRITE(NAME(9:9),'(I1)') I
          CALL GCB_CAN( NAME, STATUS )
        END DO
        CALL GCB_FSAVE( IFID, STATUS )
        CALL GCB_DETACH( STATUS )
      END IF

*  History file entry
      CALL HSI_ADD( OFID, VERSION, STATUS )
      CALL USI_NAMES( 'I', IFILES, STATUS )
      CALL HSI_PTXTI( OFID, IFILES, .TRUE., STATUS )
      CALL MSG_SETI( 'NDEG', NDEG )
      IF ( POLY ) THEN
        CALL MSG_MAKE( 'Polynomial fit of degree ^NDEG produced.',
     :                                                HTXT, N )
      ELSE
        CALL MSG_MAKE( 'Polynomial of degree ^NDEG subtracted.',
     :                                              HTXT, N )
      END IF
      CALL HSI_PTXT( OFID, 1, HTXT, STATUS )

*  Tidy up
 99   CALL AST_CLOSE()
      CALL AST_ERR( STATUS )

      END



*+  POLYFIT_WEIGHTS - Uses DATA_ERROR & DATA_QUALITY info to calc weights.
      SUBROUTINE POLYFIT_WEIGHTS( VAROK, QUALOK, NDAT, BLEN, VAR, QUAL,
     :                                          WT, BAD, NBAD, STATUS )
*    Description :
*      If data errors are available then a set of weights=1/(error)**2 is
*      set up in array WT.
*      If data quality information is available (LOG=.TRUE.) then this
*      is scanned and any bad (non-zero quality) data are given zero
*      weight.
*    History :
*     date:  original (institution::username)
*     30 Mar 87: NODDY_PRO header added (pla@uk.ac.bham.sr/star)
*    Type Definitions :
      IMPLICIT NONE
*    Global constants :
      INCLUDE 'SAE_PAR'
*    Import :
      INTEGER                NDAT            ! No. of data points per fit
      INTEGER                BLEN            ! No. of fits.

      LOGICAL                VAROK           ! Is quality present & ok ?
      LOGICAL                QUALOK          ! Is quality present & ok ?
*    Import-Export :
      REAL                   VAR(NDAT,BLEN)  ! Variance information (if present)

      LOGICAL                QUAL(NDAT,BLEN) ! Quality information (if present)
*
*    Export :
*
      REAL                   WT(NDAT,BLEN)   ! Array of errors.

      INTEGER                BAD(BLEN)       ! Number of bad data points per fit
      INTEGER                NBAD            ! Total number of bad data points.
*
*    Status :
*
      INTEGER STATUS
*
*    Local variables :
*
      INTEGER                I, J            ! Dummy variables for loops.
*-

*    Check status
      IF  ( STATUS .NE. SAI__OK ) RETURN

      NBAD = 0
      CALL ARR_INIT1I(0, BLEN, BAD, STATUS)

      IF (QUALOK .AND. VAROK) THEN
        DO J = 1, BLEN
          DO I = 1, NDAT
            IF (QUAL(I,J)) THEN
              WT(I,J) = 1.0 / VAR(I,J)

            ELSE
              NBAD    = NBAD + 1
              BAD(J)  = BAD(J) + 1
              WT(I,J) = 0.0

            END IF
          END DO
        END DO
      ELSE IF (QUALOK) THEN
        DO J = 1, BLEN
          DO I = 1, NDAT
            IF (QUAL(I,J)) THEN
              WT(I,J) = 1.0

            ELSE
              NBAD     = NBAD + 1
              BAD(J)   = BAD(J) + 1
              WT(I,J)  = 0.0

            END IF
          END DO
        END DO
      ELSE IF (VAROK) THEN
        DO J = 1, BLEN
          DO I = 1, NDAT
            WT(I,J) = 1.0 / VAR(I,J)
          END DO
        END DO
      END IF
      END



*+  POLYFIT_LOOPWT - Loop over dataset performing 1d weighted fits
      SUBROUTINE POLYFIT_LOOPWT (POLY, NDEG, NDIM, NDAT, BLEN, AXIS,
     :                                BAD, WEIGHT, DATA, PARAM,STATUS)
*    Description :
*     Loops ofer n dimensioal dataset performing 1d fits
*    Environment parameters :
*    Method :
*     Calls POLYFIT_DOIT to do the 1d fits
*    Deficiencies :
*    Bugs :
*    Authors :
*     Phil Andrews (BHVAD::PLA)
*    History :
*     11/11/88:  Original (PLA)
*    Type definitions :
      IMPLICIT NONE
*    Global constants :
      INCLUDE 'SAE_PAR'
*    Import :
      INTEGER                NDEG               ! Degree of polynomial fitted
      INTEGER                NDIM               ! Dimensionality of data
      INTEGER                NDAT               ! No. of points per fit
      INTEGER                BLEN               ! No. of fits
      INTEGER                BAD(BLEN)          ! Number of bad data points per fit

      REAL                   AXIS(NDAT)         ! Axis(1) data
      REAL                   WEIGHT(NDAT, BLEN) ! Weight array

*    Import - Export :
      REAL                   DATA(NDAT, BLEN)   ! Data array on exit contains either
                                                ! original data - fit (POLY = .FALSE.)
                                                ! or fit (POLY = .TRUE.)
      REAL                   PARAM(*)       ! Coeffs of polynomial fitted to normalized axis
*    Status :
      INTEGER STATUS
*
*    Functions :
*
      REAL                   POLYFIT_CNM        ! n! / ( m! (n-m)! )
*
*    Local variables :
*
      INTEGER                I, B, C         ! Loop counters
      INTEGER                XNPTR           ! Normalized & exponensiated X values (used by
                                             ! subroutine POLYFIT_DOIT)
      INTEGER                SIZE(2)         ! Size of XN array

      LOGICAL                POLY            ! Selects DTREND or POLYFIT function.


      REAL                   AXMAX, AXMIN    ! Max & min axis values
      REAL                   C1, C2, FIT     ! Used in calculating fit coefficients to real axis
      REAL COEFF(12)
*-

*  Check status
      IF ( STATUS .NE. SAI__OK ) RETURN

*  Set up array for normalized axis
      SIZE(1) = NDAT
      SIZE(2) = (2 * NDEG) + 1

      CALL DYN_MAPR (2, SIZE, XNPTR, STATUS)

      SIZE(2) = SIZE(2) - 1

*    Check status.
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*    Write normalized axis
      CALL POLYFIT_NORM( SIZE(1), SIZE(2), AXIS, %VAL(XNPTR),
     :                                         AXMAX, AXMIN )

*    Loop over fits
      DO I = 1, BLEN
        IF (NDAT - BAD(I) .GT. NDEG) THEN
*        Perform either polynomial fit or detrending:
          CALL POLYFIT_DOIT( POLY, SIZE(1), SIZE(2), NDEG, .TRUE.,
     :                       WEIGHT(1,I), %VAL(XNPTR), DATA(1,I),
     :                       COEFF, STATUS )

        END IF
      END DO

      IF (NDIM .EQ. 1) THEN
*      Display the fit
        CALL MSG_PRNT ('The fit determined was:')

        C1 = 2.0 / (AXMAX - AXMIN)
        C2 = (- AXMAX - AXMIN) / (AXMAX - AXMIN)

*      Loop over all output coefficients
        DO B = 0, NDEG
          FIT = 0.0

*        Loop over coeffs of fit to normalized data
          DO C = B, NDEG
            FIT = FIT + (COEFF(C+1) * (C1**C) * POLYFIT_CNM(C, B)
     :                * ((C2 / C1)**(C - B) ))

          END DO

          PARAM(B+1)=FIT

          IF (B .EQ. 0) THEN
            CALL MSG_SETR ('FIT', FIT)
            CALL MSG_SETI ('N', B)

            IF (B .NE. NDEG) THEN
              CALL MSG_PRNT ('y = ^FIT * X**^N +')

            ELSE
              CALL MSG_PRNT ('y = ^FIT * X**^N')

            END IF
          ELSE IF (B .LT. NDEG) THEN
            CALL MSG_SETR ('FIT', FIT)
            CALL MSG_SETI ('N', B)
            CALL MSG_PRNT ('    ^FIT * X**^N +')

         ELSE
            CALL MSG_SETR ('FIT', FIT)
            CALL MSG_SETI ('N', B)
            CALL MSG_PRNT ('    ^FIT * X**^N')

          END IF
        END DO
      END IF

 99   IF ( STATUS .NE. SAI__OK ) THEN
        CALL AST_REXIT( 'POLYFIT_LOOPWT', STATUS )
      END IF

      END



*+  POLYFIT_LOOPNOWT - Loop over dataset performing 1d unweighted fits
      SUBROUTINE POLYFIT_LOOPNOWT (POLY, NDEG, NDIM, NDAT, BLEN, AXIS,
     :                                            DATA,PARAM, STATUS)
*    Description :
*     Loops ofer n dimensioal dataset performing 1d fits
*    Environment parameters :
*    Method :
*     Calls POLYFIT_DOIT to do the 1d fits
*    Deficiencies :
*    Bugs :
*    Authors :
*     Phil Andrews (BHVAD::PLA)
*    History :
*     11 Nov 88:  Original (PLA)
*    Type definitions :
      IMPLICIT NONE
*    Global constants :
      INCLUDE 'SAE_PAR'
*    Import :
      INTEGER                NDEG               ! Degree of polynomial fitted
      INTEGER                NDIM               ! Dimensionality of data
      INTEGER                NDAT               ! No. of points per fit
      INTEGER                BLEN               ! No. of fits

      REAL                   AXIS(NDAT)         ! Axis(1) data

*    Import - Export :
      REAL                   DATA(NDAT, BLEN)   ! Data array on exit contains either
                                                ! original data - fit (POLY = .FALSE.)
                                                ! or fit (POLY = .TRUE.)
      REAL                   PARAM(*)       ! Coeffs of polynomial fitted to normalized axis
*    Status :
      INTEGER STATUS
*
*    Functions :
*
      REAL                   POLYFIT_CNM       ! n! / ( m! (n-m)! )
*
*    Local variables :
*
      INTEGER                I, B, C         ! Loop counters
      INTEGER                XNPTR           ! Normalized & exponensiated X values (used by
                                             ! subroutine POLYFIT_DOIT)
      INTEGER                SIZE(2)         ! Size of XN array

      LOGICAL                POLY            ! Selects DTREND or POLYFIT function.


      REAL                   AXMAX, AXMIN    ! Max & min axis values
      REAL                   C1, C2, FIT     ! Used in calculating fit coefficients to real axis
      REAL COEFF(12)
*-

*    Check status
      IF ( STATUS .NE. SAI__OK ) RETURN

*    Set up array for normalized axis
      SIZE(1) = NDAT
      SIZE(2) = (2 * NDEG) + 1

      CALL DYN_MAPR (2, SIZE, XNPTR, STATUS)

      SIZE(2) = SIZE(2) - 1

*    Check status
      IF ( STATUS .NE. SAI__OK ) GOTO 99

*  Write normalized axis
      CALL POLYFIT_NORM( SIZE(1), SIZE(2), AXIS, %VAL(XNPTR), AXMAX,
     :                                                       AXMIN )

*  Loop over fits
      DO I = 1, BLEN
*      Perform either polynomial fit or detrending:
        CALL POLYFIT_DOIT (POLY, SIZE(1), SIZE(2), NDEG, .FALSE., C1,
     :                            %VAL(XNPTR), DATA(1,I), COEFF, STATUS)

      END DO

      IF (NDIM .EQ. 1) THEN
*      Display the fit
        CALL MSG_PRNT ('The fit determined was:')

        C1 = 2.0 / (AXMAX - AXMIN)
        C2 = (- AXMAX - AXMIN) / (AXMAX - AXMIN)

*      Loop over all output coefficients
        DO B = 0, NDEG
          FIT = 0.0

*        Loop over coeffs of fit to normalized data
          DO C = B, NDEG
            FIT = FIT + (COEFF(C+1) * (C1**C) * POLYFIT_CNM(C, B)
     :                * ((C2 / C1)**(C - B) ))

          END DO

          PARAM(B+1)=FIT

          IF (B .EQ. 0) THEN
            CALL MSG_SETR ('FIT', FIT)
            CALL MSG_SETI ('N', B)

            IF (B .NE. NDEG) THEN
              CALL MSG_PRNT ('y = ^FIT * X**^N +')

            ELSE
              CALL MSG_PRNT ('y = ^FIT * X**^N')

            END IF
          ELSE IF (B .LT. NDEG) THEN
            CALL MSG_SETR ('FIT', FIT)
            CALL MSG_SETI ('N', B)
            CALL MSG_PRNT ('    ^FIT * X**^N +')

         ELSE
            CALL MSG_SETR ('FIT', FIT)
            CALL MSG_SETI ('N', B)
            CALL MSG_PRNT ('    ^FIT * X**^N')

          END IF
        END DO
      END IF

 99   IF (STATUS .NE. SAI__OK) THEN
        CALL AST_REXIT( 'POLYFIT_LOOP', STATUS )
      END IF

      END






*+  POLYFIT_NORM - Normalizes AXIS then exponensiates.
      SUBROUTINE POLYFIT_NORM (SIZE1, SIZE2, AXIS, XN, AXMAX, AXMIN)
*    Description :
*     Normalizes the AXIS using:
*       new_value = (2*value - MAX - MIN) / (MAX - MIN)
*     Exponensiates
*    History :
*     14/11/88:  original (PLA)
*    Type definitions :
      IMPLICIT NONE

*    Import :
      INTEGER    SIZE1, SIZE2         ! Size of XN array

      REAL       AXIS (SIZE1)         ! AXIS values.

*    Export :
      REAL       XN(SIZE1, 0:SIZE2)   ! Normalized axis values.
      REAL       AXMAX                ! Max value of X array.
      REAL       AXMIN                ! Min value of X array.

*    Local variables :
      INTEGER    I, J                 ! Loop counters
*-
      AXMIN = AXIS(1)
      AXMAX = AXIS(SIZE1)

      IF (AXMIN .GT. AXMAX) THEN
        AXMIN = AXIS(SIZE1)
        AXMAX = AXIS(1)
      END IF

      IF (SIZE2 .GT. 1) THEN
        DO I = 1, SIZE1
          XN(I, 0) = 1.0
          XN(I, 1) = (2 * AXIS(I) - AXMAX - AXMIN) / (AXMAX - AXMIN)

          DO J = 2, SIZE2
            IF (ABS(XN(I,1)) .GT. 0.0) THEN
              XN(I,J) = XN(I,1)**J

            ELSE
              XN(I,J) = 0.0

            END IF
          END DO
        END DO
      ELSE IF (SIZE2 .EQ. 1) THEN
        DO I = 1, SIZE1
          XN(I, 0) = 1.0
          XN(I, 1) = (2 * AXIS(I) - AXMAX - AXMIN) / (AXMAX - AXMIN)

        END DO
      ELSE IF (SIZE2 .EQ. 0) THEN
        DO I = 1, SIZE1
          XN(I, 0) = 1.0

        END DO
      END IF
      END



*+  POLYFIT_DOIT - Fits a polynomial to the DATA, replacing it with residuals or fit
      SUBROUTINE POLYFIT_DOIT (POLY, SIZE1, SIZE2, NDEG, WFIT, WEIGHT,
     :                                       XN, DATA, COEFF, STATUS )
*    Description :
*      Fits a polynomial to the DATA, replacing the data with either
*      DATA = fit value (POLY = TRUE), or
*      DATA = original data - fit value (POLY = FALSE)
*    Method :
*     From some ancient SAO routine no understands now!
*    Deficiencies :
*    Bugs :
*    Authors :
*     Trevor Ponman (BHVAD::TJP)
*     Phillip Andrews (BHVAD::PLA)
*    History :
*
*     16 Sep 86 : Version 4 (TJP)
*      1 Apr 87 : Code structured, header added. (PLA)
*      7 May 88 : Asterix88 version    (LTVAD::RDS)
*     17 Jun 88 : Improved the efficiency by merging two DO loops so
*                 that powers are only calculated once    (LTVAD::RDS)
*     16 Nov 88 : Normalization extracted to suit needs of POLYFIT. Code
*                 improved both for readability and speed. (PLA)
*      7 Oct 92 : Change SIZE to SIZE1/2 to accomodate change it
*                 compiler ages ago (DJA)
*
*    Type Definitions :
*
      IMPLICIT NONE
*
*    Global constants :
*
      INCLUDE 'SAE_PAR'
*
*    Status :
*
      INTEGER    STATUS
*
*    Import :
*
      LOGICAL    POLY                ! if true return fit, if not return residuals
      LOGICAL    WFIT                ! Is weighted fit required.

      INTEGER    SIZE1, SIZE2        ! Size of XN array
      INTEGER    NDEG                ! Degree of polynomial ( max.power of X )

      REAL       XN(SIZE1,0:SIZE2)   ! Normalized axis values
      REAL       WEIGHT (*)          ! Array of weights proportional to inverse
*
*    Import-Export :
*
      REAL       DATA (SIZE1)      ! Array of dependent variable becomes fit or resuduals
*
*    Export :
*
      REAL       COEFF (NDEG+2)      ! Work space, before subtraction.
                                     ! First NDEG+1 elements return coefficients
                                     ! of polynomial in XN (in ascending powers)
      REAL       SUMSQ               ! Sum of squares of weighted residuals
*
*    Local variables :
*
      INTEGER    I                   ! Dummy variable for loops.
      INTEGER    II                  !   "      "      "    "
      INTEGER    J                   !   "      "      "    "
      INTEGER    NA                  ! NDEG + 1
      INTEGER    NC                  ! NDEG + 2
      INTEGER    M                   ! (2*NDEG) + 1

      REAL       WORK1(12,12)        ! Work arrays used by
      REAL       WORK2(21)           ! this subroutine
      REAL       DMEAN               ! Mean value of DATA array.
      REAL       DCONST              ! Constant component in DATA fit.
      REAL       DENOM               ! Used in calculation of YMEAN.
      REAL       SUMBIT              ! Used to speed up the code

*-

*   Calc mean if NDEG = 0
      IF (NDEG .EQ. 0) THEN
        DMEAN = 0.0

        IF (WFIT) THEN
          DENOM = 0.0

          DO I = 1, SIZE1
            DMEAN = DMEAN + DATA(I) * WEIGHT(I)
            DENOM = DENOM + WEIGHT(I)

          END DO
          DMEAN = DMEAN / DENOM                ! Weighted mean

        ELSE
          DO I = 1, SIZE1
            DMEAN = DMEAN + DATA(I)

          END DO
          DMEAN = DMEAN / REAL(SIZE1)        ! Unweighted mean

        END IF
        COEFF(1) = DMEAN
        SUMSQ    = 0.0

        DO I = 1, SIZE1
          IF (POLY) THEN
            DATA(I) = DMEAN

          ELSE
            DATA(I) = DATA(I) - DMEAN

          END IF
        END DO
      ELSE
*      Start of sao program: initialize variables.
        NA = NDEG + 1
        NC = NDEG + 2
        M  = (2 * NA) - 1

        IF (WFIT) THEN
*        Set the (NDEG + 2)th elements of WORK1, and first NA elements of WORK2
          DO I = 1, NA
            WORK1 (I, NC) = 0.0
            WORK2 (I)     = 0.0
            II            = I - 1

            DO J = 1, SIZE1
              SUMBIT       = WEIGHT(J) * XN(J, II)
              WORK2(I)     = WORK2(I) + SUMBIT
              WORK1(I, NC) = WORK1(I, NC) + (DATA(J) * SUMBIT)

            END DO
          END DO

*        Set up rest of WORK2
          DO I = NA + 1, M
            WORK2(I) = 0.0
            II       = I - 1

            DO J = 1, SIZE1
              SUMBIT   = WEIGHT(J) * XN(J,II)
              WORK2(I) = WORK2(I) + SUMBIT

            END DO
          END DO

*        Set up rest of WORK1
          DO I = 1, NA
            DO J = 1, NA
              WORK1 (I, J) = WORK2 (I + J - 1)

            END DO
          END DO
        ELSE
*        Set the (NDEG + 2)th elements of WORK1, and first NA elements of WORK2
          DO I = 1, NA
            WORK1(I, NC) = 0.0
            WORK2(I)     = 0.0
            II           = I - 1

            DO J = 1, SIZE1
              SUMBIT       = XN(J,II)
              WORK2 (I)    = WORK2 (I) + SUMBIT
              WORK1 (I,NC) = WORK1 (I,NC) + (DATA(J) * SUMBIT)

            END DO
          END DO

*        Set up rest of WORK2
          DO I = NA + 1, M
            WORK2(I) = 0.0
            II       = I - 1

            DO J = 1, SIZE1
              SUMBIT   = XN(J,II)
              WORK2(I) = WORK2(I) + SUMBIT

            END DO
          END DO

*        Set up rest of WORK1
          DO I = 1, NA
            DO J = 1, NA
              WORK1 (I, J) = WORK2 (I + J - 1)

            END DO
          END DO
        END IF

        CALL POLYFIT_TRIANG (NA,    NC,     WORK1, STATUS)
        CALL POLYFIT_SOLVE  (WORK1, NA, NC, COEFF, STATUS)

*      Check status
        IF (STATUS .NE. SAI__OK) GOTO 99

        DO I = 1, SIZE1
          DCONST = COEFF(1)

          DO J = 2, NA
            DCONST = DCONST + (COEFF(J) * XN(I,J-1))
          END DO

          IF (.NOT. POLY) THEN
            DATA (I) = DATA (I) - DCONST                     ! Return residual

          ELSE
            DATA (I) = DCONST	                             ! Return calc y val

          END IF
        END DO
      END IF

 99   CONTINUE

      END



*+  POLYFIT_TRIANG - Triangularizes the matrix A, with M rows & M+N columns.
      SUBROUTINE POLYFIT_TRIANG (M, NC, A, STATUS)
*    Description :
*      A is the matrix with M rows and M + 1 columns to be triangularised.
*    History :
*     ????????: Original (BHVAD::TJP)
*     1 Apr 87: Structured and header added. (PLA)
*     7 May 88: Argument order changed.    (LTVAD::RDS)
*    Type Definitions :
      IMPLICIT NONE
      INCLUDE 'SAE_PAR'
*    Status :
      INTEGER    STATUS
*    Import :
      INTEGER    NC           !NDEG + 2
      INTEGER    M            !No. of rows in matrix

*    Import-Export :
      REAL       A(12,12)     !Matrix to be triangularized.
*    Local variables :
      INTEGER    J            ! Dummy variable for loops.
      INTEGER    K            ! Dummy variable for loops.
      INTEGER    L            ! Dummy variable for loops.
      INTEGER    NUMCOLUMNS   ! No. of columns.
      INTEGER    MAXX         ! Max value of X?

      REAL       VALUE        ! Current element of matrix A.
*-

*   Check status
      IF (STATUS .NE. SAI__OK) RETURN

*   Initialize variables.
      NUMCOLUMNS = M + 1

      DO J = 1, M - 1
        MAXX  = J
        VALUE = ABS(A(J, J))

        DO K = (J + 1), M
          IF (VALUE - ABS(A(K, J)) .LT. 0) THEN
            VALUE = ABS(A(K, J))
            MAXX  = K

          END IF
        END DO

        DO K = J, NUMCOLUMNS
          VALUE      = A(MAXX, K)
          A(MAXX, K) = A(J, K)
          A(J, K)    = VALUE

        END DO
        VALUE = A(J, J)

        IF (VALUE .EQ. 0) GOTO 99

        DO K = J, NUMCOLUMNS
          A(J, K) = A(J, K) / VALUE

        END DO

        DO K = (J + 1), M
          VALUE = A(K, J)

          DO L = J, NUMCOLUMNS
            A(K, L) = A(K, L) - (VALUE * A(J, L))

          END DO
        END DO
      END DO
      VALUE = A(M, M)

      IF (VALUE .EQ. 0) GOTO 99

      DO K = M, NUMCOLUMNS
        A(M, K) = A(M, K) / VALUE

      END DO

 99   IF (VALUE .EQ. 0) THEN
        STATUS = SAI__ERROR
      END IF
      
      END




*+  POLYFIT_SOLVE - Converts (M,M+1) triangularized matrix to (M) matrix.
      SUBROUTINE POLYFIT_SOLVE (A, M, NC, X, STATUS)
*    Description :
*      A is the triangularised matrix with M rows and M+1 columns.
*      X is the solution matrix with M rows.
*    History :
*     1 Apr 87: Structured, tidied, header added. (pla@uk.ac.bham.sr.star)
*     7 Jun 88: Order of arguments changed.
*    Type Definitions :
      IMPLICIT NONE
      INCLUDE 'SAE_PAR'
*    Status
      INTEGER    STATUS
*    Import :
      INTEGER    NC           !NDEG + 2
      INTEGER    M            ! No. of rows in triangularized matrix.

      REAL       A(12,12)     ! Triangularized matrix.
*    Export :
      REAL       X(NC)        ! Solution matrix.
*    Local variables :
      INTEGER    K            ! Dummy variable for loop.
      INTEGER    I            !   "      "      "    "
      INTEGER    MP1          ! M + 1
      INTEGER    MMK          ! M - K

      REAL       SUM          ! Used in calculation.
*-

*    Check status
      IF (STATUS .NE. SAI__OK) RETURN

      MP1 = M + 1

      DO K = 1, (M - 1)
        MMK  = M - K
        X(M) = A(M, MP1) / A(M,M)
        SUM  = 0.0

        DO I = MMK + 1, M
          SUM = SUM + A(MMK, I) * X(I)
        END DO
        X(MMK) = A(MMK,MP1) - SUM

      END DO
      
      END


*+  POLYFIT_CNM - Returns n! / (m! (n-m)!)
      REALFUNCTION POLYFIT_CNM( N, M )
*    Description :
*     RETURNS      N! / (M! (N-M)1)
*    Deficiencies :
*    Bugs :
*    Authors :
*     Phillip Andrews (PLA_AST88@uk.ac.bham.sr/star)
*    History :
*     7/10/88:  Original (PLA_AST88)
*    Type definitions :
      IMPLICIT NONE
*    Global constants :
      INCLUDE 'SAE_PAR'
*    Import :
      INTEGER                N, M

*    Local variables :
      REAL                   FAC(0:10)                     ! Factorial (n) for n = 0 to 10

*    Local data
      DATA  FAC /1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0,
     :                                     40320.0, 362880.0, 3628800.0/
*-

      POLYFIT_CNM = FAC(N) / ( FAC(M) * FAC(N-M) )

      END