C NCLFORTSTART
      subroutine clustr ( x, d, dev, b, f, e, i, j, n, nz, k )
      integer b(i)
      integer e(k)
      double precision d(k,j)
      double precision f(i)
      double precision dev(k)
      double precision x(i,j)
C NCLEND
c*********************************************************************72
c
cc CLUSTR uses the K-means algorithm to cluster data.
c
c  Discussion:
c
c    Given a matrix of I observations on J variables, the
c    observations are allocated to N clusters in such a way that the
c    within-cluster sum of squares is minimised.
c
c  Modified:
c
c    04 February 2008
c
c  Author:
c
c    David Sparks
c    Modifications by John Burkardt
c
c    Reference:
c
c    David Sparks,
c    Algorithm AS 58:
c    Euclidean Cluster Analysis,
c    Applied Statistics,
c    Volume 22, Number 1, 1973, pages 126-130.
c
c  Parameters:
c
c    Input, double precision X(I,J), the observed data.
c
c    Input/output, double precision D(K,J), the cluster centers.  On input,
c    the user has chosen these.  On output, they have been updated.
c
c    Output, double precision DEV(K), the sums of squared deviations of 
c    observations from their cluster centers.
c
c    Output, integer B(I), indicates the cluster to which each observation
c    has been assigned.
c
c    Workspace, double precision F(I).
c
c    Output, integer E(K), the number of observations assigned to each cluster.
c
c    Input, integer I, the number of observations.
c
c    Input, integer J, the number of variables.
c
c    Input, integer N, the number of clusters.
c
c    Input, integer NZ, the minimum number of observations which any cluster
c    is allowed to have.
c
c    Input, integer K, the maximum number of clusters.
c
c      implicit none      
      double precision big      
      double precision da
      double precision db
      double precision dc
      double precision de
      double precision fl
      double precision fm
      double precision fq
      integer ia
      integer ic
      integer id
      integer ie
      integer ig
      integer ih
      integer ii
      integer ij
      integer ik
      integer il
      integer in
      integer ip
      integer ir
      integer is
      integer it
      integer iu
      integer iw
      integer ix
      integer iy
      double precision one 
      double precision two
      parameter ( big = 1.0D+10 )
      parameter ( one = 1.0D+00 )
      parameter ( two = 2.0D+00 )    

      do ia = 1, n
        e(ia) = 0
      end do
c
c  For each observation, calculate the distance from each cluster
c  center, and assign to the nearest.
c
      do ic = 1, i
        f(ic) = 0.0D+00
        da = big
        do id = 1, n
          db = 0.0D+00
          do ie=1, j
            dc = x(ic,ie) - d(id,ie)
            db = db + dc * dc
            if ( da .le. db ) then
              go to 30
            end if
          end do
          da = db
          b(ic) = id
   30     continue
        end do
        ig = b(ic)
        e(ig) = e(ig) + 1
      end do
c
c  Calculate the mean and sum of squares for each cluster.
c
      do ix = 1, n
        dev(ix) = 0.0D+00
        do iy = 1, j
          d(ix,iy) = 0.0D+00
        end do
      end do

      do ic = 1, i
        ig = b(ic)
        do ih = 1, j
          d(ig,ih) = d(ig,ih) + x(ic,ih)
        end do
      end do

      do ij = 1, j
        do ii = 1, n
          if ( 0 .lt. e(ii) ) then
            d(ii,ij) = d(ii,ij) / real ( e(ii) )
          end if
        end do
      end do

      do ij = 1, j
        do ik = 1, i
          il = b(ik)
          da = x(ik,ij) - d(il,ij)
          db = da * da
          f(ik) = f(ik) + db
          dev(il) = dev(il) + db
        end do
      end do

      do ik = 1, i
        il = b(ik)
        fl = e(il)
        if ( 2.0D+00 .le. fl ) then
          f(ik) = f(ik) * fl / ( fl - one )
        end if
      end do
c
c  Examine each observation in turn to see if it should be
c  reassigned to a different cluster.
c
      if ( nz .le. 0 ) then
        nz = 1
      end if

   90 continue

      iw = 0

      do ik = 1, i

        il = b(ik)
        ir = il
c
c  If the number of cluster points is less than or equal to the
c  specified minimum, NZ, then bypass this iteration.
c
        if ( nz .lt. e(il) ) then

          fl = e(il)
          dc = f(ik)

          do in = 1, n
            if ( in .ne. il ) then
              fm = e(in)
              fm = fm / ( fm + one )
              de = 0.0D+00
              do ip = 1, j
                da = x(ik,ip) - d(in,ip)
                de = de + da * da * fm
                if ( dc .le. de ) then
                  go to 100
                end if
              end do
              dc = de
              ir = in
  100         continue
            end if
          end do
c
c  Reassignment is made here if necessary.
c
          if ( ir .ne. il ) then

            fq = e(ir)
            dev(il) = dev(il) - f(ik)
            dev(ir) = dev(ir) + dc
            e(ir) = e(ir) + 1
            e(il) = e(il) - 1

            do is = 1, j
              d(il,is) = ( d(il,is) * fl - x(ik,is) ) / ( fl - one )
              d(ir,is) = ( d(ir,is) * fq + x(ik,is) ) / ( fq + one )
            end do

            b(ik) = ir

            do it = 1, i

              ij = b(it)

              if ( ij .eq. il .or. ij .eq. ir ) then
                f(it) = 0.0D+00
                do iu = 1, j
                  da = x(it,iu) - d(ij,iu)
                  f(it) = f(it) + da * da
                end do
                fl = e(ij)
                f(it) = f(it) * fl / ( fl - one )
              end if

            end do

            iw = iw + 1

          end if

        end if

      end do
c
c  If any reassignments were made on this pass, then do another pass.
c
      if ( iw .ne. 0 ) then
        go to 90
      end if

      return
      end

      subroutine timestamp ( )

c*********************************************************************72
c
cc TIMESTAMP prints out the current YMDHMS date as a timestamp.
c
c  Discussion:
c
c    This FORTRAN77 version is made available for cases where the
c    FORTRAN90 version cannot be used.
c
c  Modified:
c
c    12 January 2007
c
c  Author:
c
c    John Burkardt
c
c  Parameters:
c
c    None
c
c      implicit none
      character * ( 8 ) ampm
      integer d
      character * ( 8 ) date
      integer h
      integer m
      integer mm
      character * ( 9 ) month(12)
      integer n
      integer s
      character * ( 10 ) time
      integer y

      
      save month

      data month /
     &  'January  ', 'February ', 'March    ', 'April    ', 
     &  'May      ', 'June     ', 'July     ', 'August   ', 
     &  'September', 'October  ', 'November ', 'December ' /

      call date_and_time ( date, time )

      read ( date, '(i4,i2,i2)' ) y, m, d
      read ( time, '(i2,i2,i2,1x,i3)' ) h, n, s, mm

      if ( h .lt. 12 ) then
        ampm = 'AM'
      else if ( h .eq. 12 ) then
        if ( n .eq. 0 .and. s .eq. 0 ) then
          ampm = 'Noon'
        else
          ampm = 'PM'
        end if
      else
        h = h - 12
        if ( h .lt. 12 ) then
          ampm = 'PM'
        else if ( h .eq. 12 ) then
          if ( n .eq. 0 .and. s .eq. 0 ) then
            ampm = 'Midnight'
          else
            ampm = 'AM'
          end if
        end if
      end if

      write ( *, 
     &  '(i2,1x,a,1x,i4,2x,i2,a1,i2.2,a1,i2.2,a1,i3.3,1x,a)' ) 
     &  d, month(m), y, h, ':', n, ':', s, '.', mm, ampm

      return
      end
