!
!  here are the all FORTRAN packing routines from
!  NCAR.  they should work on all machines
!
!  note: we are using the slowest version  of these routines.
!  min and max values are determined and the numbers packed
!  between these values. (we're using packaf, see the NCAR routines).
!
!  installed into the interface: 5 MAY 1992, wcs
!
!  first, our local interface (wrlcm, rdlcm) to the NCAR routines
!


SUBROUTINE wrlcm (arr,apr,nw,npack) 5,3
  INCLUDE 'agricpu.inc'
!
!
! ON INPUT:
!  ARR(NW)        AN ARRAY OF REAL NUMBERS
!  NW             ITS LENGTH
!
! ON OUTPUT:
!  APR(1)         MINIMUM OF ARR.
!  APR(2)         THE SCALE FACTOR=S(NPACK-1)/(2*MAX(ABS(ARR)))
!  APR(.)         THE PACKED DATA
!
  DIMENSION arr(nw),apr(*)

!  print*,' npack in wrlcm =' , npack

  IF( npack < 1 .OR. npack > 4 ) THEN
    PRINT*,' wrong value of npack'
    STOP
  END IF
!
  IF (npack == 1) THEN
    DO i=1,nw
      apr(i) = arr(i)
    END DO
  ELSE
    cpu0 = f_cputime()
    CALL packaf( arr,nw,apr,npack,sca1,sca2 )
    cpu_pack = cpu_pack + f_cputime() - cpu0
  END IF
  RETURN
END SUBROUTINE wrlcm
!


SUBROUTINE rdlcm (arr,apr,nw,npack) 5,2
  INCLUDE 'agricpu.inc'
!
!
! ON INPUT:
!  APR(.)         THE PACKED ARRAY PRODUCED BY WRLCM
!  NW             THE NUMBER OF ELEMENTS WANTED
!
! ON EXIT:
!  ARR(NW)        THE ARRAY OF REAL NUMBERS
!
!
  DIMENSION arr(nw),apr(*)
!
  IF (npack == 1) THEN
    DO i=1,nw
      arr(i) = apr(i)
    END DO
  ELSE
    cpu0 = f_cputime()
    CALL unpkaf( arr,nw,apr,npack,sca1,sca2 )
    cpu_unpack = cpu_unpack + f_cputime() - cpu0
  END IF
!
  RETURN
END SUBROUTINE rdlcm
!
!  here are the NCAR routines.
!
!***********************************************************************
!
! latest revision       September, 1991.  See "purpose" section
!                    immediately below, and "revision history"
!                    comments at the end of these routines for
!                    more details.
!
! purpose               This version of the packing/unpacking routines
!                    was written in Fortran 77, to be used on CRAY
!                    machines, including XMP, YMP, and CRAY-2 series.
!                    It is intended that this version will replace
!                    the mixed Fortran/Cal version that has been
!                    traditional on NCAR's XMP/YMP machines.
!
!                    This all-Fortran version produces slightly
!                    different results than the FORTRAN/CAL version
!                    unless rounding is turned off on the YMP compiler
!                    cf77 (option"r").  This option is not available
!                    on CRAY-2 machines, which is the reason that the
!                    two sets of routines produce different results.
!
!                    Historically, the purpose of these routines is
!                    to reduce the data volume and possibly the core
!                    requirements associated with a user program by
!                    packing/unpacking several values per Cray word.
!                    See the "algorithm" section below for details.
!
! usage                 call packaf (arn,nrn,apn,npk,amn,amx)
!                    call packbf (arn,nrn,apn,npk,amn,amx)
!                    call packcf (arn,nrn,apn,npk,amn,amx)
!                    call unpkaf (arn,nrn,apn,npk,amn,amx)
!                    call unpkbf (arn,nrn,apn,npk,amn,amx)
!                    call unpkcf (arn,nrn,apn,npk,amn,amx)
!
! arguments             All of the packing and unpacking routines
!                    have the same calling sequence, with
!                    arguments arn, nrn, apn, npk, amn, and amx,
!                    the last two of which may be omitted
!                    except in calls to packcf.  The arguments
!                    may be described as follows:
!
! on input              arn(i),i=1,nrn
!
!                    an array of real numbers - input to the
!                    packers, output from the unpacker
!
!                    nrn     the length of arn - input
!
!                    apn(1)    minimum value - amn
!                    apn(2)    scale factor -
!                              (2**(64/npk)-1)/(amx-amn)
!
!
!                    npk
!
!                    packing density - number of data to be packed
!                    in one output word (2, 3, or 4) - input
!
!                    amn
!
!                    minimum - input to packcf, output from others
!                    (may be omitted from all except packcf calls)
!
!                    amx
!
!                    maximum - input to packcf, output from others
!                    (may be omitted from all except packcf calls)
!
! on output             apn(j) , j=3,4,...n
!
!                    the packed data - npk integers per word -
!                    n = 2+(nrn+npk-1)/npk - output from the
!                    packers, input to the unpacker
!
! special conditions    There are three packing routines to
!                    choose from - packaf, packbf, and packcf.
!                    They differ only in the way in which
!                    amn and amx are computed.
!
!                    Packaf is the safest, but the slowest.
!                    amn and amx are computed by the routine
!                    itself.  Initially, amn is the actual
!                    minimum and amx is the actual maximum.
!                    then, if amn = amx, the values are
!                    adjusted outwards to avoid division by zero.
!                    further, if amn amx differ in sign,
!                    amx is adjusted outwards a little to
!                    ensure that zeroes in the original array
!                    will be returned by the unpacker as zeroes
!                    (mathematically, at least - hardware
!                    round-off makes it impossible to ensure
!                    the return of exact zeroes without
!                    seriously degrading the speed of the
!                    routine - however, the value returned should
!                    never be greater than 1.e-14*abs(amn) ).
!
!                    Packbf is the intermediate choice, faster
!                    but not as safe. amn and amx are computed
!                    by the routine itself.
!                    Initially, amx is set equal to the maximum
!                    absolute value and amn to the negative
!                    of that.  Then, amn and amx are adjusted
!                    as for packaf.  This routine should probably
!                    be used only for data which is known to have
!                    a distribution which is roughly symmetric
!                    around zero.
!
!                    Packcf is the fastest, but most dangerous,
!                    of the three. the user must supply the
!                    initial values of amn and amx.  These values
!                    are not adjusted in any way.
!                    No test is made to ensure that data
!                    values lie between amn and amx - errors may
!                    result.
!
!                    Routines 'unpkaf', 'unpkbf', and 'unpkcf'
!                    are used for the unpacking process.
!                    Actually, they are all synonymns, provided
!                    for the sake of consistency if and when
!                    planned extensions are added to this package.
!
!                    Note that, in any case, these routines
!                    must be used with care.  One wildly
!                    out-of-range datum can destroy the precision
!                    of the rest.  Moreover, data sets with
!                    unlike ranges should be packed separately.
!
! i/o                   none
!
! precision             see algorithm discussion below
!
! required library      none
! files
!
! language              fortran
!
! history               1979: Written by Vince Wayland of Cray Research,
!                    Inc, and made available to NCAR in the same year.
!                    1991: (September) Re-written entirely in fortran
!                    for use on CRAY-2 machines, and also XMP/YMP
!                    machines if desired.  The two versions produce
!                    different results on the XMP/YMP series, as
!                    explained above in the "purpose" section.
!
! algorithm             Given an array of real numbers
!                    (arn(i), i=1,nrn), a packing density
!                    npk (with value 2, 3, or 4), and real numbers
!                    amn and amx (the former less than or equal
!                    to any datum in arn and the latter greater
!                    than or equal to any datum in arn), one can
!                    reduce each element of arn to a positive
!                    integer less than or equal to
!                    2**(64/npk)-1 by means of the
!                    following formula:
!
!                    int(i) = ifix( (arn(i)-amn) *
!                             (2**(64/npk)-1) / (amx-amn) + .5 )
!
!                    the numbers amn and (2**(64/npk)-1) / (amx-amn)
!                    may then be stored as apn(1) and apn(2), followed
!                    by the integers (int(i),i=1,nrn), packed npk per
!                    word in apn(3) through apn(2+(nrn+npk-1)/npk)).
!                    The original (arn(i),i=1,nrn) may subsequently
!                    be reconstructed from the contents of the packed
!                    array apn by means of the following formula:
!
!                    arn(i) = amn + int(i) * (amx-amn) /
!                             (2**(64/npk)-1)
!
!                    Note that the relative error of the reconstructed
!                    values is a function of the packing density.
!                    The error being 1. / (2**(64/npk)-1), i.e.
!                    2.3 e-9, 4.8 e-7, 1.5 e-5 for npk 2, 3, 4
!                    respectively.
!
! portability           not portable.
!
! timing                as of September, 1992 the following timings
!                    obtained on a Cray-YMP8/864.
!
!                    each time given is the time required
!                    to pack or unpack 100,000 numbers, in milliseconds.
!
!                    routine     npk=2      npk=3      npk=4
!                    -------     -----      -----      -----
!
!                    packaf       4.97       5.03       5.11
!                    packbf       3.60       3.67       3.76
!                    packcf       2.06       2.12       2.21
!                    unpkbf       1.79       1.46       1.30
!
!***********************************************************************


SUBROUTINE packaf (arn,nrn,apn,npk,amn,amx) 1,3
  DIMENSION arn(nrn),apn(*),po2(3)
  DATA po2 / 4294967295.,2097151.,65535. /
!
! check for certain errors in the arguments.
!
  noa=numarg()
  IF (noa < 4 .OR. noa > 6) GO TO 901
!
  IF (nrn <= 0) GO TO 902
!
  IF (npk < 2 .OR. npk > 4) GO TO 903
!
  xmn=0577777777777777777777B
  xmx=1577777777777777777777B
!
  DO i = 1, nrn
    xmn=MIN(xmn,arn(i))
    xmx=MAX(xmx,arn(i))
  END DO
!
! adjust the values of xmn and xmx - make sure they are not equal.
!
  102 IF (xmn /= xmx) GO TO 104
  xmn=xmn-.5*ABS(xmn)
  xmx=xmx+.5*ABS(xmx)
  IF (xmn /= xmx) GO TO 104
  xmn=-1.
  xmx=+1.
!
! if xmn and xmx differ in sign, further adjust them so that zero will
! be a possible output from the unpacker.
!
  104 IF (xmn*xmx >= 0.) GO TO 105
  IF (ABS(xmn) < ABS(xmx)) THEN
    xmn=xmx-xmx*po2(npk-1)/FLOAT(IFIX( xmx*po2(npk-1)/(xmx-xmn)))
  ELSE
    xmx=xmn-xmn*po2(npk-1)/FLOAT(IFIX(-xmn*po2(npk-1)/(xmx-xmn)))
  END IF
!
! return the computed values of xmn and xmx to the user, if appropriate.
!
  105 IF (noa >= 5) amn=xmn
  IF (noa >= 6) amx=xmx
!
! store the minimum and the scale factor in the output array.
!
  apn(1)=xmn
  apn(2)=po2(npk-1)/(xmx-xmn)
!
! call an appropriate routine to do the actual packing
!
  IF (npk == 2) THEN
    CALL pack2f (arn, nrn, apn)
  ELSE IF (npk == 3) THEN
    CALL pack3f (arn, nrn, apn)
  ELSE
    CALL pack4f (arn, nrn, apn)
  END IF
!
  RETURN
!
! error exits.
!
  901 PRINT 9001
  STOP
!
  902 PRINT 9002
  STOP
!
  903 PRINT 9003
  STOP
!
! formats.
!
  9001 FORMAT (' packaf - wrong number of arguments - stop ')
  9002 FORMAT (' packaf - array length .le. 0 - stop ')
  9003 FORMAT (' packaf - packing density .ne. 2, 3, or 4 - stop ')
!
END SUBROUTINE packaf


SUBROUTINE packbf (arn,nrn,apn,npk,amn,amx),3
  DIMENSION arn(nrn),apn(*),po2(3)
  DATA po2 / 4294967295.,2097151.,65535. /
!
! check for certain errors in the arguments.
!
  noa=numarg()
  IF (noa < 4 .OR. noa > 6) GO TO 901
!
  IF (nrn <= 0) GO TO 902
!
!  print*,' npk =', npk

  IF (npk < 2 .OR. npk > 4) GO TO 903
!
  xmx=1577777777777777777777B
!
  DO i = 1, nrn
    xmx=MAX(xmx,arn(i))
  END DO
!
  102 xmn=-xmx
!
! adjust the values of xmn and xmx - make sure they are not equal.
!
  IF (xmn /= xmx) GO TO 103
  xmn=-1.
  xmx=+1.
!
! xmn and xmx differ in sign.  further adjust them so that zero will
! be a possible output from the unpacker.
!
  103 xmx=xmn-xmn*po2(npk-1)/FLOAT(IFIX(-xmn*po2(npk-1)/(xmx-xmn)))
!
! return the computed values of xmn and xmx to the user, if appropriate.
!
  104 IF (noa >= 5) amn=xmn
  IF (noa >= 6) amx=xmx
!
! store the minimum and the scale factor in the output array.
!
  apn(1)=xmn
  apn(2)=po2(npk-1)/(xmx-xmn)
!
! call an appropriate routine to do the actual packing
!
  IF (npk == 2) THEN
    CALL pack2f (arn, nrn, apn)
  ELSE IF (npk == 3) THEN
    CALL pack3f (arn, nrn, apn)
  ELSE
    CALL pack4f (arn, nrn, apn)
  END IF
!
  RETURN
! error exits.
!
  901 PRINT 9001
  STOP
!
  902 PRINT 9002
  STOP
!
  903 PRINT 9003
  STOP
!
! formats.
!
  9001 FORMAT (' packbf - wrong number of arguments - stop ')
  9002 FORMAT (' packbf - array length .le. 0 - stop ')
  9003 FORMAT (' packbf - packing density .ne. 2, 3, or 4 - stop ')
!
END SUBROUTINE packbf


SUBROUTINE packcf (arn,nrn,apn,npk,amn,amx),3
!
! this is the subroutine packcf.
!
  DIMENSION arn(nrn),apn(*),po2(3)
  DATA po2 / 4294967295.,2097151.,65535. /
!
! check for certain errors in the arguments.
!
  noa=numarg()
  IF (noa /= 6) GO TO 901
!
  IF (nrn <= 0) GO TO 902
!
  IF (npk < 2 .OR. npk > 4) GO TO 903
!
  IF (amn >= amx) GO TO 904
!
! store the minimum and the scale factor in the output array.
!
  apn(1)=amn
  apn(2)=po2(npk-1)/(amx-amn)
!
! call an appropriate routine to do the actual packing
!
  IF (npk == 2) THEN
    CALL pack2f (arn, nrn, apn)
  ELSE IF (npk == 3) THEN
    CALL pack3f (arn, nrn, apn)
  ELSE
    CALL pack4f (arn, nrn, apn)
  END IF
!
  RETURN
!
! error exits.
!
  901 PRINT 9001
  STOP
!
  902 PRINT 9002
  STOP
!
  903 PRINT 9003
  STOP
!
  904 PRINT 9004
  STOP
!
! formats.
!
  9001 FORMAT (' packcf - wrong number of arguments - stop ')
  9002 FORMAT (' packcf - array length .le. 0 - stop ')
  9003 FORMAT (' packcf - packing density .ne. 2, 3, or 4 - stop ')
  9004 FORMAT (' packcf - minimum .ge. maximum - stop ')
!
END SUBROUTINE packcf


SUBROUTINE pack2f (arn, nrn, apn) 3
!
! This routine packs 2 floats to the word
!
  DIMENSION arn (nrn), apn (*)
  npc = nrn / 2
  xsf = apn(1)
  xsc = apn(2)
  DO j = 1, npc
    i = j * 2 - 1
    ih = (arn(i  ) - xsf) * xsc + 0.5
    il = (arn(i+1) - xsf) * xsc + 0.5
    apn(j+2) = OR (shiftl (ih, 32), il)
  END DO
!
! Pack up any left-overs
!
  IF (npc * 2 /= nrn) THEN
    j = npc + 1
    i = j * 2 - 1
    ih = (arn(i  ) - xsf) * xsc + 0.5
    apn(j+2) = OR (shiftl (ih, 32), 0)
  END IF
END SUBROUTINE pack2f


SUBROUTINE pack3f (arn, nrn, apn) 3
!
! This routine packs 3 floats to the word
!
  DIMENSION arn (nrn), apn (*)
  npc = nrn / 3
  xsf = apn(1)
  xsc = apn(2)
  DO j = 1, npc
    i = j * 3 - 2
    ih = (arn(i  ) - xsf) * xsc + 0.5
    im = (arn(i+1) - xsf) * xsc + 0.5
    il = (arn(i+2) - xsf) * xsc + 0.5
    apn(j+2) = OR (shiftl (ih, 42),                                     &
                   OR (shiftl (im, 21), il))
  END DO
!
! Pack up any left-overs
!
  m = nrn - (npc * 3)
  IF (m /= 0) THEN
    j = npc + 1
    i = j * 3 - 2
    ih = (arn(i  ) - xsf) * xsc + 0.5
    IF (m == 1) THEN
      im = 0
    ELSE
      im = (arn(i+1) - xsf) * xsc + 0.5
    END IF
    apn(j+2) = OR (shiftl (ih, 42),                                     &
                   shiftl (im, 21))
  END IF
END SUBROUTINE pack3f


SUBROUTINE pack4f (arn, nrn, apn) 3
!
! This routine packs 4 floats to the word
!
  DIMENSION arn (nrn), apn (*)
  npc = nrn / 4
  xsf = apn(1)
  xsc = apn(2)
  DO j = 1, npc
    i = j * 4 - 3
    ihh = (arn(i  ) - xsf) * xsc + 0.5
    ihl = (arn(i+1) - xsf) * xsc + 0.5
    ilh = (arn(i+2) - xsf) * xsc + 0.5
    ill = (arn(i+3) - xsf) * xsc + 0.5
    apn(j+2) = OR (shiftl (ihh, 48),                                    &
               OR (shiftl (ihl, 32),                                    &
               OR (shiftl (ilh, 16), ill)))
  END DO
!
! Pack up any left-overs
!
  m = nrn - (npc * 4)
  IF (m > 0) THEN
    j = npc + 1
    i = j * 4 - 3
    ihh = (arn(i  ) - xsf) * xsc + 0.5
    ilh = 0
    ihl = 0
    GO TO (112, 111, 110) M
    110    ilh = (arn(i+2) - xsf) * xsc + 0.5
    111    ihl = (arn(i+1) - xsf) * xsc + 0.5
    112    CONTINUE
    apn(j+2) = OR (shiftl (ihh, 48),                                    &
               OR (shiftl (ihl, 32), shiftl (ilh, 16)))
  END IF
END SUBROUTINE pack4f


SUBROUTINE unpack2f (arn, nrn, apn) 1
!
! This routine unpacks 2 floats from each word
!
  DIMENSION arn (nrn), apn (*)
  npc = nrn / 2
  xsf = apn(1)
  xsc = 1.0 / apn(2)
  DO j = 1, npc
    i = j * 2 - 1
    ih =      shiftr (apn(j+2), 32)
    il = AND (        apn(j+2), 4294967295)
    arn(i  ) = ih * xsc + xsf
    arn(i+1) = il * xsc + xsf
  END DO
!
! Unpack any left-overs
!
  m = nrn - (npc * 2)
  IF (m > 0) THEN
    j = npc + 1
    i = j * 2 - 1
    ih = shiftr (apn(j+2), 32)
    arn(i) = ih * xsc + xsf
  END IF
END SUBROUTINE unpack2f


SUBROUTINE unpack3f (arn, nrn, apn) 1
!
! This routine unpacks 3 floats from each word
!
  DIMENSION arn (nrn), apn (*)
  npc = nrn / 3
  xsf = apn(1)
  xsc = 1.0 / apn(2)
  DO j = 1, npc
    i = j * 3 - 2
    ih =      shiftr (apn(j+2), 42)
    im = AND (shiftr (apn(j+2), 21), 2097151)
    il = AND (        apn(j+2),      2097151)
    arn(i  ) = ih * xsc + xsf
    arn(i+1) = im * xsc + xsf
    arn(i+2) = il * xsc + xsf
  END DO
!
! Unpack any left-overs
!
  m = nrn - (npc * 3)
  IF (m > 0) THEN
    j = npc + 1
    i = j * 3 - 2
    ih = shiftr (apn(j+2), 42)
    arn(i) = ih * xsc + xsf
    IF (m > 1) THEN
      im = AND (shiftr (apn(j+2), 21), 2097151)
      arn(i+1) = im * xsc + xsf
    END IF
  END IF
END SUBROUTINE unpack3f


SUBROUTINE unpack4f (arn, nrn, apn) 1
!
! This routine unpacks 4 floats from each word
!
  DIMENSION arn (nrn), apn (*)
  npc = nrn / 4
  xsf = apn(1)
  xsc = 1.0 / apn(2)
  DO j = 1, npc
    i = j * 4 - 3
    ihh =      shiftr (apn(j+2), 48)
    ihl = AND (shiftr (apn(j+2), 32), 65535)
    ilh = AND (shiftr (apn(j+2), 16), 65535)
    ill = AND (        apn(j+2),      65535)
    arn(i  ) = ihh * xsc + xsf
    arn(i+1) = ihl * xsc + xsf
    arn(i+2) = ilh * xsc + xsf
    arn(i+3) = ill * xsc + xsf
  END DO
!
! Unpack any left-overs
!
  m = nrn - (npc * 4)
  IF (m > 0) THEN
    j = npc + 1
    i = j * 4 - 3
    ihh =      shiftr (apn(j+2), 48)
    ihl = AND (shiftr (apn(j+2), 32), 65535)
    ilh = AND (shiftr (apn(j+2), 16), 65535)
    GO TO (112, 111, 110) M
    110    arn(i+2) = ilh * xsc + xsf
    111    arn(i+1) = ihl * xsc + xsf
    112    arn(i  ) = ihh * xsc + xsf
  END IF
END SUBROUTINE unpack4f


SUBROUTINE unpkbf (arn,nrn,apn,npk,amn,amx),3
!
! this is the subroutine unpkbf.
!
  DIMENSION arn(nrn),apn(*),msk(3)
  DATA msk / 4294967295 , 2097151 , 65535 /
!
! 'unpkaf', and 'unpkcf' are all synonyms for 'unpkbf'.
!
  ENTRY unpkaf (arn,nrn,apn,npk,amn,amx)
  ENTRY unpkcf (arn,nrn,apn,npk,amn,amx)
!
! check for errors in the input.
!
  noa=numarg()
  IF (noa < 4 .OR. noa > 6) GO TO 901
!
  IF (nrn <= 0) GO TO 902
!
  IF (npk < 2 .OR. npk > 4) GO TO 903
!
! return the values of amn and amx, if appropriate.
!
  IF (noa >= 5) amn=apn(1)
  IF (noa >= 6) amx=apn(1)+FLOAT(msk(npk-1))/apn(2)
!
! call an appropriate routine to do the actual unpacking
!
  IF (npk == 2) THEN
    CALL unpack2f (arn, nrn, apn)
  ELSE IF (npk == 3) THEN
    CALL unpack3f (arn, nrn, apn)
  ELSE
    CALL unpack4f (arn, nrn, apn)
  END IF
!
  106 RETURN
!
! error exits.
!
  901 PRINT 9001
  STOP
!
  902 PRINT 9002
  STOP
!
  903 PRINT 9003
  STOP
!
! formats.
!
  9001 FORMAT (' unpkf(abc) - wrong number of arguments - stop ')
  9002 FORMAT (' unpkf(abc) - array length .le. 0 - stop ')
  9003 FORMAT (' unpkf(abc) - packing density .ne. 2, 3, or 4 - stop ')
!
! revision history -----------------------------------------------------
!
! september, 1991  Rewrote these routines in Fortran, for use on CRAY-2
!               machines.  This version does not produce results which
!               are identical to results produced by the mixed Fortran
!               /Cal routines used on the YMP machines. The reason is
!               stated above in the "purpose" section of the package
!               documentation.
! ----------------------------------------------------------------------
END SUBROUTINE unpkbf