kusano 7d535a
CCCCC COPYRIGHT (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
CCCCC Research Councils.    All rights reserved.
kusano 7d535a
CCCCC PACKAGE MC64A/AD
kusano 7d535a
CCCCC AUTHORS Iain Duff (i.duff@rl.ac.uk) and Jacko Koster (jak@ii.uib.no)
kusano 7d535a
CCCCC LAST UPDATE 20/09/99
kusano 7d535a
CCCCC
kusano 7d535a
C *** Conditions on external use ***
kusano 7d535a
C
kusano 7d535a
C The user shall acknowledge the contribution of this
kusano 7d535a
C package in any publication of material dependent upon the use of
kusano 7d535a
C the package. The user shall use reasonable endeavours to notify
kusano 7d535a
C the authors of the package of this publication.
kusano 7d535a
C
kusano 7d535a
C The user can modify this code but, at no time
kusano 7d535a
C shall the right or title to all or any part of this package pass
kusano 7d535a
C to the user. The user shall make available free of charge
kusano 7d535a
C to the authors for any purpose all information relating to any
kusano 7d535a
C alteration or addition made to this package for the purposes of
kusano 7d535a
C extending the capabilities or enhancing the performance of this
kusano 7d535a
C package.
kusano 7d535a
C
kusano 7d535a
C The user shall not pass this code directly to a third party without the
kusano 7d535a
C express prior consent of the authors.  Users wanting to licence their
kusano 7d535a
C own copy of these routines should send email to hsl@aeat.co.uk
kusano 7d535a
C
kusano 7d535a
C None of the comments from the Copyright notice up to and including this
kusano 7d535a
C one shall be removed or altered in any way.
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64ID(ICNTL)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
C  Purpose
kusano 7d535a
C  =======
kusano 7d535a
C
kusano 7d535a
C  The components of the array ICNTL control the action of MC64A/AD.
kusano 7d535a
C  Default values for these are set in this subroutine.
kusano 7d535a
C
kusano 7d535a
C  Parameters
kusano 7d535a
C  ==========
kusano 7d535a
C
kusano 7d535a
      INTEGER ICNTL(10)
kusano 7d535a
C
kusano 7d535a
C  Local variables
kusano 7d535a
      INTEGER I
kusano 7d535a
C
kusano 7d535a
C    ICNTL(1) has default value 6.
kusano 7d535a
C     It is the output stream for error messages. If it
kusano 7d535a
C     is negative, these messages will be suppressed.
kusano 7d535a
C
kusano 7d535a
C    ICNTL(2) has default value 6.
kusano 7d535a
C     It is the output stream for warning messages.
kusano 7d535a
C     If it is negative, these messages are suppressed.
kusano 7d535a
C
kusano 7d535a
C    ICNTL(3) has default value -1.
kusano 7d535a
C     It is the output stream for monitoring printing.
kusano 7d535a
C     If it is negative, these messages are suppressed.
kusano 7d535a
C
kusano 7d535a
C    ICNTL(4) has default value 0.
kusano 7d535a
C     If left at the defaut value, the incoming data is checked for
kusano 7d535a
C     out-of-range indices and duplicates.  Setting ICNTL(4) to any
kusano 7d535a
C     other will avoid the checks but is likely to cause problems
kusano 7d535a
C     later if out-of-range indices or duplicates are present.
kusano 7d535a
C     The user should only set ICNTL(4) non-zero, if the data is
kusano 7d535a
C     known to avoid these problems.
kusano 7d535a
C
kusano 7d535a
C    ICNTL(5) to ICNTL(10) are not used by MC64A/AD but are set to
kusano 7d535a
C     zero in this routine.
kusano 7d535a
kusano 7d535a
C Initialization of the ICNTL array.
kusano 7d535a
      ICNTL(1) = 6
kusano 7d535a
      ICNTL(2) = 6
kusano 7d535a
      ICNTL(3) = -1
kusano 7d535a
      DO 10 I = 4,10
kusano 7d535a
        ICNTL(I) = 0
kusano 7d535a
   10 CONTINUE
kusano 7d535a
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64AD(JOB,N,NE,IP,IRN,A,NUM,CPERM,LIW,IW,LDW,DW,
kusano 7d535a
     &           ICNTL,INFO)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
C  Purpose
kusano 7d535a
C  =======
kusano 7d535a
C
kusano 7d535a
C This subroutine attempts to find a column permutation for an NxN 
kusano 7d535a
C sparse matrix A = {a_ij} that makes the permuted matrix have N 
kusano 7d535a
C entries on its diagonal.
kusano 7d535a
C If the matrix is structurally nonsingular, the subroutine optionally
kusano 7d535a
C returns a column permutation that maximizes the smallest element 
kusano 7d535a
C on the diagonal, maximizes the sum of the diagonal entries, or 
kusano 7d535a
C maximizes the product of the diagonal entries of the permuted matrix.
kusano 7d535a
C For the latter option, the subroutine also finds scaling factors 
kusano 7d535a
C that may be used to scale the matrix so that the nonzero diagonal 
kusano 7d535a
C entries of the permuted matrix are one in absolute value and all the 
kusano 7d535a
C off-diagonal entries are less than or equal to one in absolute value.
kusano 7d535a
C The natural logarithms of the scaling factors u(i), i=1..N, for the 
kusano 7d535a
C rows and v(j), j=1..N, for the columns are returned so that the 
kusano 7d535a
C scaled matrix B = {b_ij} has entries b_ij = a_ij * EXP(u_i + v_j).
kusano 7d535a
C
kusano 7d535a
C  Parameters
kusano 7d535a
C  ==========
kusano 7d535a
C
kusano 7d535a
      INTEGER JOB,N,NE,NUM,LIW,LDW
kusano 7d535a
      INTEGER IP(N+1),IRN(NE),CPERM(N),IW(LIW),ICNTL(10),INFO(10)
kusano 7d535a
      DOUBLE PRECISION A(NE),DW(LDW)
kusano 7d535a
C
kusano 7d535a
C JOB is an INTEGER variable which must be set by the user to
kusano 7d535a
C control the action. It is not altered by the subroutine.
kusano 7d535a
C Possible values for JOB are:
kusano 7d535a
C   1 Compute a column permutation of the matrix so that the
kusano 7d535a
C     permuted matrix has as many entries on its diagonal as possible. 
kusano 7d535a
C     The values on the diagonal are of arbitrary size. HSL subroutine 
kusano 7d535a
C     MC21A/AD is used for this. See [1].
kusano 7d535a
C   2 Compute a column permutation of the matrix so that the smallest 
kusano 7d535a
C     value on the diagonal of the permuted matrix is maximized.
kusano 7d535a
C     See [3].
kusano 7d535a
C   3 Compute a column permutation of the matrix so that the smallest
kusano 7d535a
C     value on the diagonal of the permuted matrix is maximized.
kusano 7d535a
C     The algorithm differs from the one used for JOB = 2 and may
kusano 7d535a
C     have quite a different performance. See [2].
kusano 7d535a
C   4 Compute a column permutation of the matrix so that the sum
kusano 7d535a
C     of the diagonal entries of the permuted matrix is maximized.
kusano 7d535a
C     See [3].
kusano 7d535a
C   5 Compute a column permutation of the matrix so that the product
kusano 7d535a
C     of the diagonal entries of the permuted matrix is maximized
kusano 7d535a
C     and vectors to scale the matrix so that the nonzero diagonal 
kusano 7d535a
C     entries of the permuted matrix are one in absolute value and 
kusano 7d535a
C     all the off-diagonal entries are less than or equal to one in 
kusano 7d535a
C     absolute value. See [3].
kusano 7d535a
C  Restriction: 1 <= JOB <= 5.
kusano 7d535a
C 
kusano 7d535a
C N is an INTEGER variable which must be set by the user to the
kusano 7d535a
C   order of the matrix A. It is not altered by the subroutine.
kusano 7d535a
C   Restriction: N >= 1.
kusano 7d535a
C
kusano 7d535a
C NE is an INTEGER variable which must be set by the user to the
kusano 7d535a
C   number of entries in the matrix. It is not altered by the 
kusano 7d535a
C   subroutine.
kusano 7d535a
C   Restriction: NE >= 1.
kusano 7d535a
C
kusano 7d535a
C IP is an INTEGER array of length N+1.
kusano 7d535a
C   IP(J), J=1..N, must be set by the user to the position in array IRN 
kusano 7d535a
C   of the first row index of an entry in column J. IP(N+1) must be set
kusano 7d535a
C   to NE+1. It is not altered by the subroutine.
kusano 7d535a
C
kusano 7d535a
C IRN is an INTEGER array of length NE. 
kusano 7d535a
C   IRN(K), K=1..NE, must be set by the user to hold the row indices of
kusano 7d535a
C   the entries of the matrix. Those belonging to column J must be 
kusano 7d535a
C   stored contiguously in the positions IP(J)..IP(J+1)-1. The ordering
kusano 7d535a
C   of the row indices within each column is unimportant. Repeated 
kusano 7d535a
C   entries are not allowed. The array IRN is not altered by the 
kusano 7d535a
C   subroutine.
kusano 7d535a
C
kusano 7d535a
C A is a REAL (DOUBLE PRECISION in the D-version) array of length NE. 
kusano 7d535a
C   The user must set A(K), K=1..NE, to the numerical value of the 
kusano 7d535a
C   entry that corresponds to IRN(K). 
kusano 7d535a
C   It is not used by the subroutine when JOB = 1. 
kusano 7d535a
C   It is not altered by the subroutine.
kusano 7d535a
C
kusano 7d535a
C NUM is an INTEGER variable that need not be set by the user.
kusano 7d535a
C   On successful exit, NUM will be the number of entries on the 
kusano 7d535a
C   diagonal of the permuted matrix.
kusano 7d535a
C   If NUM < N, the matrix is structurally singular.
kusano 7d535a
C
kusano 7d535a
C CPERM is an INTEGER array of length N that need not be set by the 
kusano 7d535a
C   user. On successful exit, CPERM contains the column permutation.
kusano 7d535a
C   Column CPERM(J) of the original matrix is column J in the permuted 
kusano 7d535a
C   matrix, J=1..N.
kusano 7d535a
C
kusano 7d535a
C LIW is an INTEGER variable that must be set by the user to
kusano 7d535a
C   the dimension of array IW. It is not altered by the subroutine.
kusano 7d535a
C   Restriction:
kusano 7d535a
C     JOB = 1 :  LIW >= 5N
kusano 7d535a
C     JOB = 2 :  LIW >= 4N
kusano 7d535a
C     JOB = 3 :  LIW >= 10N + NE
kusano 7d535a
C     JOB = 4 :  LIW >= 5N
kusano 7d535a
C     JOB = 5 :  LIW >= 5N
kusano 7d535a
C 
kusano 7d535a
C IW is an INTEGER array of length LIW that is used for workspace.
kusano 7d535a
C 
kusano 7d535a
C LDW is an INTEGER variable that must be set by the user to the
kusano 7d535a
C   dimension of array DW. It is not altered by the subroutine.
kusano 7d535a
C   Restriction:
kusano 7d535a
C     JOB = 1 :  LDW is not used
kusano 7d535a
C     JOB = 2 :  LDW >= N
kusano 7d535a
C     JOB = 3 :  LDW >= NE
kusano 7d535a
C     JOB = 4 :  LDW >= 2N + NE
kusano 7d535a
C     JOB = 5 :  LDW >= 3N + NE
kusano 7d535a
C 
kusano 7d535a
C DW is a REAL (DOUBLE PRECISION in the D-version) array of length LDW 
kusano 7d535a
C   that is used for workspace. If JOB = 5, on return,
kusano 7d535a
C   DW(i) contains u_i, i=1..N, and DW(N+j) contains v_j, j=1..N.
kusano 7d535a
C 
kusano 7d535a
C ICNTL is an INTEGER array of length 10. Its components control the 
kusano 7d535a
C   output of MC64A/AD and must be set by the user before calling
kusano 7d535a
C   MC64A/AD. They are not altered by the subroutine.
kusano 7d535a
C
kusano 7d535a
C   ICNTL(1) must be set to specify the output stream for
kusano 7d535a
C   error messages. If ICNTL(1) < 0, messages are suppressed.
kusano 7d535a
C   The default value set by MC46I/ID is 6.
kusano 7d535a
C
kusano 7d535a
C   ICNTL(2) must be set by the user to specify the output stream for
kusano 7d535a
C   warning messages. If ICNTL(2) < 0, messages are suppressed.
kusano 7d535a
C   The default value set by MC46I/ID is 6.
kusano 7d535a
C
kusano 7d535a
C   ICNTL(3) must be set by the user to specify the output stream for
kusano 7d535a
C   diagnostic messages. If ICNTL(3) < 0, messages are suppressed.
kusano 7d535a
C   The default value set by MC46I/ID is -1.
kusano 7d535a
C
kusano 7d535a
C   ICNTL(4) must be set by the user to a value other than 0 to avoid
kusano 7d535a
C   checking of the input data.
kusano 7d535a
C   The default value set by MC46I/ID is 0.
kusano 7d535a
C    
kusano 7d535a
C INFO is an INTEGER array of length 10 which need not be set by the 
kusano 7d535a
C   user. INFO(1) is set non-negative to indicate success. A negative 
kusano 7d535a
C   value is returned if an error occurred, a positive value if a 
kusano 7d535a
C   warning occurred. INFO(2) holds further information on the error.
kusano 7d535a
C   On exit from the subroutine, INFO(1) will take one of the 
kusano 7d535a
C   following values:
kusano 7d535a
C    0 : successful entry (for structurally nonsingular matrix).
kusano 7d535a
C   +1 : successful entry (for structurally singular matrix).
kusano 7d535a
C   +2 : the returned scaling factors are large and may cause
kusano 7d535a
C        overflow when used to scale the matrix. 
kusano 7d535a
C        (For JOB = 5 entry only.)
kusano 7d535a
C   -1 : JOB < 1 or JOB > 5.  Value of JOB held in INFO(2).
kusano 7d535a
C   -2 : N < 1.  Value of N held in INFO(2).
kusano 7d535a
C   -3 : NE < 1. Value of NE held in INFO(2).
kusano 7d535a
C   -4 : the defined length LIW violates the restriction on LIW.
kusano 7d535a
C        Value of LIW required given by INFO(2).
kusano 7d535a
C   -5 : the defined length LDW violates the restriction on LDW.
kusano 7d535a
C        Value of LDW required given by INFO(2).
kusano 7d535a
C   -6 : entries are found whose row indices are out of range. INFO(2)
kusano 7d535a
C        contains the index of a column in which such an entry is found.
kusano 7d535a
C   -7 : repeated entries are found. INFO(2) contains the index of a 
kusano 7d535a
C        column in which such entries are found.
kusano 7d535a
C  INFO(3) to INFO(10) are not currently used and are set to zero by 
kusano 7d535a
C        the routine.
kusano 7d535a
C
kusano 7d535a
C References:
kusano 7d535a
C  [1]  I. S. Duff, (1981),
kusano 7d535a
C       "Algorithm 575. Permutations for a zero-free diagonal",
kusano 7d535a
C       ACM Trans. Math. Software 7(3), 387-390.
kusano 7d535a
C  [2]  I. S. Duff and J. Koster, (1998),
kusano 7d535a
C       "The design and use of algorithms for permuting large
kusano 7d535a
C       entries to the diagonal of sparse matrices",
kusano 7d535a
C       SIAM J. Matrix Anal. Appl., vol. 20, no. 4, pp. 889-901.
kusano 7d535a
C  [3]  I. S. Duff and J. Koster, (1999),
kusano 7d535a
C       "On algorithms for permuting large entries to the diagonal 
kusano 7d535a
C       of sparse matrices",
kusano 7d535a
C       Technical Report RAL-TR-1999-030, RAL, Oxfordshire, England.
kusano 7d535a
kusano 7d535a
C Local variables and parameters
kusano 7d535a
      INTEGER I,J,K
kusano 7d535a
      DOUBLE PRECISION FACT,ZERO,RINF
kusano 7d535a
      PARAMETER (ZERO=0.0D+00)
kusano 7d535a
C External routines and functions
kusano 7d535a
c     EXTERNAL FD05AD
kusano 7d535a
c     DOUBLE PRECISION FD05AD
kusano 7d535a
      EXTERNAL MC21AD,MC64BD,MC64RD,MC64SD,MC64WD, DLAMCH
kusano 7d535a
      DOUBLE PRECISION DLAMCH
kusano 7d535a
C Intrinsic functions
kusano 7d535a
      INTRINSIC ABS,LOG
kusano 7d535a
kusano 7d535a
C Set RINF to largest positive real number (infinity)
kusano 7d535a
c XSL    RINF = FD05AD(5)
kusano 7d535a
      RINF = DLAMCH('Overflow')
kusano 7d535a
kusano 7d535a
C Check value of JOB
kusano 7d535a
      IF (JOB.LT.1 .OR. JOB.GT.5) THEN
kusano 7d535a
        INFO(1) = -1
kusano 7d535a
        INFO(2) = JOB
kusano 7d535a
        IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9001) INFO(1),'JOB',JOB
kusano 7d535a
        GO TO 99
kusano 7d535a
      ENDIF
kusano 7d535a
C Check value of N
kusano 7d535a
      IF (N.LT.1) THEN
kusano 7d535a
        INFO(1) = -2
kusano 7d535a
        INFO(2) = N
kusano 7d535a
        IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9001) INFO(1),'N',N
kusano 7d535a
        GO TO 99
kusano 7d535a
      ENDIF
kusano 7d535a
C Check value of NE
kusano 7d535a
      IF (NE.LT.1) THEN
kusano 7d535a
        INFO(1) = -3
kusano 7d535a
        INFO(2) = NE
kusano 7d535a
        IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9001) INFO(1),'NE',NE
kusano 7d535a
        GO TO 99
kusano 7d535a
      ENDIF
kusano 7d535a
C Check LIW
kusano 7d535a
      IF (JOB.EQ.1) K = 5*N
kusano 7d535a
      IF (JOB.EQ.2) K = 4*N
kusano 7d535a
      IF (JOB.EQ.3) K = 10*N + NE
kusano 7d535a
      IF (JOB.EQ.4) K = 5*N
kusano 7d535a
      IF (JOB.EQ.5) K = 5*N
kusano 7d535a
      IF (LIW.LT.K) THEN
kusano 7d535a
        INFO(1) = -4
kusano 7d535a
        INFO(2) = K
kusano 7d535a
        IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9004) INFO(1),K
kusano 7d535a
        GO TO 99
kusano 7d535a
      ENDIF
kusano 7d535a
C Check LDW
kusano 7d535a
C If JOB = 1, do not check
kusano 7d535a
      IF (JOB.GT.1) THEN
kusano 7d535a
        IF (JOB.EQ.2) K = N
kusano 7d535a
        IF (JOB.EQ.3) K = NE
kusano 7d535a
        IF (JOB.EQ.4) K = 2*N + NE
kusano 7d535a
        IF (JOB.EQ.5) K = 3*N + NE
kusano 7d535a
        IF (LDW.LT.K) THEN
kusano 7d535a
          INFO(1) = -5
kusano 7d535a
          INFO(2) = K
kusano 7d535a
          IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9005) INFO(1),K
kusano 7d535a
          GO TO 99
kusano 7d535a
        ENDIF
kusano 7d535a
      ENDIF
kusano 7d535a
      IF (ICNTL(4).EQ.0) THEN
kusano 7d535a
C Check row indices. Use IW(1:N) as workspace
kusano 7d535a
        DO 3 I = 1,N
kusano 7d535a
          IW(I) = 0
kusano 7d535a
    3   CONTINUE
kusano 7d535a
        DO 6 J = 1,N
kusano 7d535a
          DO 4 K = IP(J),IP(J+1)-1
kusano 7d535a
            I = IRN(K)
kusano 7d535a
C Check for row indices that are out of range
kusano 7d535a
            IF (I.LT.1 .OR. I.GT.N) THEN
kusano 7d535a
              INFO(1) = -6
kusano 7d535a
              INFO(2) = J
kusano 7d535a
              IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9006) INFO(1),J,I
kusano 7d535a
              GO TO 99
kusano 7d535a
            ENDIF
kusano 7d535a
C Check for repeated row indices within a column
kusano 7d535a
            IF (IW(I).EQ.J) THEN
kusano 7d535a
              INFO(1) = -7
kusano 7d535a
              INFO(2) = J
kusano 7d535a
              IF (ICNTL(1).GE.0) WRITE(ICNTL(1),9007) INFO(1),J,I 
kusano 7d535a
              GO TO 99
kusano 7d535a
            ELSE
kusano 7d535a
              IW(I) = J
kusano 7d535a
            ENDIF
kusano 7d535a
    4     CONTINUE
kusano 7d535a
    6   CONTINUE
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Print diagnostics on input
kusano 7d535a
      IF (ICNTL(3).GE.0) THEN
kusano 7d535a
        WRITE(ICNTL(3),9020) JOB,N,NE
kusano 7d535a
        WRITE(ICNTL(3),9021) (IP(J),J=1,N+1)
kusano 7d535a
        WRITE(ICNTL(3),9022) (IRN(J),J=1,NE)
kusano 7d535a
        IF (JOB.GT.1) WRITE(ICNTL(3),9023) (A(J),J=1,NE)
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Set components of INFO to zero
kusano 7d535a
      DO 8 I=1,10
kusano 7d535a
        INFO(I) = 0
kusano 7d535a
    8 CONTINUE
kusano 7d535a
kusano 7d535a
C Compute maximum matching with MC21A/AD
kusano 7d535a
      IF (JOB.EQ.1) THEN
kusano 7d535a
C Put length of column J in IW(J)
kusano 7d535a
        DO 10 J = 1,N
kusano 7d535a
          IW(J) = IP(J+1) - IP(J)
kusano 7d535a
   10   CONTINUE
kusano 7d535a
C IW(N+1:5N) is workspace
kusano 7d535a
        CALL MC21AD(N,IRN,NE,IP,IW(1),CPERM,NUM,IW(N+1))
kusano 7d535a
        GO TO 90
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Compute bottleneck matching
kusano 7d535a
      IF (JOB.EQ.2) THEN
kusano 7d535a
C IW(1:5N), DW(1:N) are workspaces
kusano 7d535a
        CALL MC64BD(N,NE,IP,IRN,A,CPERM,NUM,
kusano 7d535a
     &     IW(1),IW(N+1),IW(2*N+1),IW(3*N+1),DW)
kusano 7d535a
        GO TO 90
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Compute bottleneck matching
kusano 7d535a
      IF (JOB.EQ.3) THEN
kusano 7d535a
C Copy IRN(K) into IW(K), ABS(A(K)) into DW(K), K=1..NE
kusano 7d535a
        DO 20 K = 1,NE
kusano 7d535a
          IW(K) = IRN(K)
kusano 7d535a
          DW(K) = ABS(A(K))
kusano 7d535a
   20   CONTINUE
kusano 7d535a
C Sort entries in each column by decreasing value. 
kusano 7d535a
        CALL MC64RD(N,NE,IP,IW,DW)
kusano 7d535a
C IW(NE+1:NE+10N) is workspace
kusano 7d535a
        CALL MC64SD(N,NE,IP,IW(1),DW,CPERM,NUM,IW(NE+1),
kusano 7d535a
     &     IW(NE+N+1),IW(NE+2*N+1),IW(NE+3*N+1),IW(NE+4*N+1),
kusano 7d535a
     &     IW(NE+5*N+1),IW(NE+6*N+1))
kusano 7d535a
        GO TO 90
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
      IF (JOB.EQ.4) THEN
kusano 7d535a
        DO 50 J = 1,N
kusano 7d535a
          FACT = ZERO
kusano 7d535a
          DO 30 K = IP(J),IP(J+1)-1
kusano 7d535a
            IF (ABS(A(K)).GT.FACT) FACT = ABS(A(K))
kusano 7d535a
   30     CONTINUE
kusano 7d535a
          DO 40 K = IP(J),IP(J+1)-1
kusano 7d535a
            DW(2*N+K) = FACT - ABS(A(K))
kusano 7d535a
   40     CONTINUE
kusano 7d535a
   50   CONTINUE
kusano 7d535a
C B = DW(2N+1:2N+NE); IW(1:5N) and DW(1:2N) are workspaces
kusano 7d535a
        CALL MC64WD(N,NE,IP,IRN,DW(2*N+1),CPERM,NUM,
kusano 7d535a
     &     IW(1),IW(N+1),IW(2*N+1),IW(3*N+1),IW(4*N+1),
kusano 7d535a
     &     DW(1),DW(N+1))
kusano 7d535a
        GO TO 90
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
      IF (JOB.EQ.5) THEN
kusano 7d535a
        DO 75 J = 1,N
kusano 7d535a
          FACT = ZERO
kusano 7d535a
          DO 60 K = IP(J),IP(J+1)-1
kusano 7d535a
            DW(3*N+K) = ABS(A(K))
kusano 7d535a
            IF (DW(3*N+K).GT.FACT) FACT = DW(3*N+K)
kusano 7d535a
   60     CONTINUE
kusano 7d535a
          DW(2*N+J) = FACT
kusano 7d535a
          IF (FACT.NE.ZERO) THEN
kusano 7d535a
            FACT = LOG(FACT)
kusano 7d535a
          ELSE
kusano 7d535a
            FACT = RINF/N
kusano 7d535a
          ENDIF
kusano 7d535a
          DO 70 K = IP(J),IP(J+1)-1
kusano 7d535a
            IF (DW(3*N+K).NE.ZERO) THEN
kusano 7d535a
              DW(3*N+K) = FACT - LOG(DW(3*N+K))
kusano 7d535a
            ELSE
kusano 7d535a
              DW(3*N+K) = RINF/N
kusano 7d535a
            ENDIF
kusano 7d535a
   70     CONTINUE
kusano 7d535a
   75   CONTINUE
kusano 7d535a
C B = DW(3N+1:3N+NE); IW(1:5N) and DW(1:2N) are workspaces
kusano 7d535a
        CALL MC64WD(N,NE,IP,IRN,DW(3*N+1),CPERM,NUM,
kusano 7d535a
     &     IW(1),IW(N+1),IW(2*N+1),IW(3*N+1),IW(4*N+1),
kusano 7d535a
     &     DW(1),DW(N+1))
kusano 7d535a
        IF (NUM.EQ.N) THEN
kusano 7d535a
          DO 80 J = 1,N
kusano 7d535a
            IF (DW(2*N+J).NE.ZERO) THEN
kusano 7d535a
              DW(N+J) = DW(N+J) - LOG(DW(2*N+J))
kusano 7d535a
            ELSE
kusano 7d535a
              DW(N+J) = ZERO
kusano 7d535a
            ENDIF
kusano 7d535a
   80     CONTINUE
kusano 7d535a
        ENDIF
kusano 7d535a
C Check size of scaling factors
kusano 7d535a
        FACT = 0.5*LOG(RINF)
kusano 7d535a
        DO 86 J = 1,N
kusano 7d535a
          IF (DW(J).LT.FACT .AND. DW(N+J).LT.FACT) GO TO 86
kusano 7d535a
          INFO(1) = 2
kusano 7d535a
          GO TO 90
kusano 7d535a
   86   CONTINUE 
kusano 7d535a
C       GO TO 90
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
   90 IF (INFO(1).EQ.0 .AND. NUM.LT.N) THEN
kusano 7d535a
C Matrix is structurally singular, return with warning
kusano 7d535a
        INFO(1) = 1
kusano 7d535a
        IF (ICNTL(2).GE.0) WRITE(ICNTL(2),9011) INFO(1)
kusano 7d535a
      ENDIF
kusano 7d535a
      IF (INFO(1).EQ.2) THEN
kusano 7d535a
C Scaling factors are large, return with warning
kusano 7d535a
        IF (ICNTL(2).GE.0) WRITE(ICNTL(2),9012) INFO(1)
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Print diagnostics on output
kusano 7d535a
      IF (ICNTL(3).GE.0) THEN
kusano 7d535a
        WRITE(ICNTL(3),9030) (INFO(J),J=1,2)
kusano 7d535a
        WRITE(ICNTL(3),9031) NUM
kusano 7d535a
        WRITE(ICNTL(3),9032) (CPERM(J),J=1,N)
kusano 7d535a
        IF (JOB.EQ.5) THEN
kusano 7d535a
          WRITE(ICNTL(3),9033) (DW(J),J=1,N)
kusano 7d535a
          WRITE(ICNTL(3),9034) (DW(N+J),J=1,N)
kusano 7d535a
        ENDIF
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Return from subroutine.
kusano 7d535a
   99 RETURN
kusano 7d535a
kusano 7d535a
 9001 FORMAT (' ****** Error in MC64A/AD. INFO(1) = ',I2,
kusano 7d535a
     &        ' because ',(A),' = ',I10)
kusano 7d535a
 9004 FORMAT (' ****** Error in MC64A/AD. INFO(1) = ',I2/
kusano 7d535a
     &        '        LIW too small, must be at least ',I8)
kusano 7d535a
 9005 FORMAT (' ****** Error in MC64A/AD. INFO(1) = ',I2/
kusano 7d535a
     &        '        LDW too small, must be at least ',I8)
kusano 7d535a
 9006 FORMAT (' ****** Error in MC64A/AD. INFO(1) = ',I2/
kusano 7d535a
     &        '        Column ',I8,
kusano 7d535a
     &        ' contains an entry with invalid row index ',I8)
kusano 7d535a
 9007 FORMAT (' ****** Error in MC64A/AD. INFO(1) = ',I2/
kusano 7d535a
     &        '        Column ',I8,
kusano 7d535a
     &        ' contains two or more entries with row index ',I8)
kusano 7d535a
 9011 FORMAT (' ****** Warning from MC64A/AD. INFO(1) = ',I2/
kusano 7d535a
     &        '        The matrix is structurally singular.')
kusano 7d535a
 9012 FORMAT (' ****** Warning from MC64A/AD. INFO(1) = ',I2/
kusano 7d535a
     &        '        Some scaling factors may be too large.')
kusano 7d535a
 9020 FORMAT (' ****** Input parameters for MC64A/AD:'/
kusano 7d535a
     &        ' JOB = ',I8/' N   = ',I8/' NE  = ',I8)
kusano 7d535a
 9021 FORMAT (' IP(1:N+1)  = ',8I8/(14X,8I8))
kusano 7d535a
 9022 FORMAT (' IRN(1:NE)  = ',8I8/(14X,8I8))
kusano 7d535a
 9023 FORMAT (' A(1:NE)    = ',4(1PD14.4)/(14X,4(1PD14.4)))
kusano 7d535a
 9030 FORMAT (' ****** Output parameters for MC64A/AD:'/
kusano 7d535a
     &        ' INFO(1:2)  = ',2I8)
kusano 7d535a
 9031 FORMAT (' NUM        = ',I8)
kusano 7d535a
 9032 FORMAT (' CPERM(1:N) = ',8I8/(14X,8I8))
kusano 7d535a
 9033 FORMAT (' DW(1:N)    = ',5(F11.3)/(14X,5(F11.3)))
kusano 7d535a
 9034 FORMAT (' DW(N+1:2N) = ',5(F11.3)/(14X,5(F11.3)))
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64BD(N,NE,IP,IRN,A,IPERM,NUM,JPERM,PR,Q,L,D) 
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER N,NE,NUM
kusano 7d535a
      INTEGER IP(N+1),IRN(NE),IPERM(N),JPERM(N),PR(N),Q(N),L(N)
kusano 7d535a
      DOUBLE PRECISION A(NE),D(N)
kusano 7d535a
kusano 7d535a
C N, NE, IP, IRN are described in MC64A/AD.
kusano 7d535a
C A is a REAL (DOUBLE PRECISION in the D-version) array of length
kusano 7d535a
C   NE. A(K), K=1..NE, must be set to the value of the entry
kusano 7d535a
C   that corresponds to IRN(K). It is not altered.
kusano 7d535a
C IPERM is an INTEGER array of length N. On exit, it contains the 
kusano 7d535a
C    matching: IPERM(I) = 0 or row I is matched to column IPERM(I).
kusano 7d535a
C NUM is INTEGER variable. On exit, it contains the cardinality of the
kusano 7d535a
C    matching stored in IPERM.
kusano 7d535a
C IW is an INTEGER work array of length 4N.
kusano 7d535a
C DW is a REAL (DOUBLE PRECISION in D-version) work array of length N.
kusano 7d535a
kusano 7d535a
C Local variables
kusano 7d535a
      INTEGER I,II,J,JJ,JORD,Q0,QLEN,IDUM,JDUM,ISP,JSP,
kusano 7d535a
     &        K,KK,KK1,KK2,I0,UP,LOW
kusano 7d535a
      DOUBLE PRECISION CSP,DI,DNEW,DQ0,AI,A0,BV
kusano 7d535a
C Local parameters 
kusano 7d535a
      DOUBLE PRECISION RINF,ZERO,MINONE
kusano 7d535a
      PARAMETER (ZERO=0.0D+0,MINONE=-1.0D+0)
kusano 7d535a
C Intrinsic functions
kusano 7d535a
      INTRINSIC ABS,MIN
kusano 7d535a
C External subroutines and/or functions
kusano 7d535a
c      EXTERNAL FD05AD,MC64DD,MC64ED,MC64FD, DLAMCH
kusano 7d535a
c      DOUBLE PRECISION FD05AD, DLAMCH
kusano 7d535a
      EXTERNAL MC64DD,MC64ED,MC64FD, DLAMCH
kusano 7d535a
      DOUBLE PRECISION DLAMCH
kusano 7d535a
kusano 7d535a
C Set RINF to largest positive real number
kusano 7d535a
c XSL  RINF = FD05AD(5)
kusano 7d535a
      RINF = DLAMCH('Overflow')
kusano 7d535a
kusano 7d535a
C Initialization
kusano 7d535a
      NUM = 0
kusano 7d535a
      BV = RINF
kusano 7d535a
      DO 10 K = 1,N
kusano 7d535a
        IPERM(K) = 0
kusano 7d535a
        JPERM(K) = 0
kusano 7d535a
        PR(K) = IP(K)
kusano 7d535a
        D(K) = ZERO
kusano 7d535a
   10 CONTINUE
kusano 7d535a
C Scan columns of matrix;
kusano 7d535a
      DO 20 J = 1,N
kusano 7d535a
        A0 = MINONE
kusano 7d535a
        DO 30 K = IP(J),IP(J+1)-1
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          AI = ABS(A(K))
kusano 7d535a
          IF (AI.GT.D(I)) D(I) = AI
kusano 7d535a
          IF (JPERM(J).NE.0) GO TO 30
kusano 7d535a
          IF (AI.GE.BV) THEN
kusano 7d535a
            A0 = BV
kusano 7d535a
            IF (IPERM(I).NE.0) GO TO 30
kusano 7d535a
            JPERM(J) = I 
kusano 7d535a
            IPERM(I) = J
kusano 7d535a
            NUM = NUM + 1
kusano 7d535a
          ELSE
kusano 7d535a
            IF (AI.LE.A0) GO TO 30
kusano 7d535a
            A0 = AI
kusano 7d535a
            I0 = I
kusano 7d535a
          ENDIF
kusano 7d535a
   30   CONTINUE
kusano 7d535a
        IF (A0.NE.MINONE .AND. A0.LT.BV) THEN
kusano 7d535a
          BV = A0
kusano 7d535a
          IF (IPERM(I0).NE.0) GO TO 20
kusano 7d535a
          IPERM(I0) = J
kusano 7d535a
          JPERM(J) = I0
kusano 7d535a
          NUM = NUM + 1
kusano 7d535a
        ENDIF
kusano 7d535a
   20 CONTINUE
kusano 7d535a
C Update BV with smallest of all the largest maximum absolute values 
kusano 7d535a
C of the rows.
kusano 7d535a
      DO 25 I = 1,N
kusano 7d535a
        BV = MIN(BV,D(I))
kusano 7d535a
   25 CONTINUE
kusano 7d535a
      IF (NUM.EQ.N) GO TO 1000
kusano 7d535a
C Rescan unassigned columns; improve initial assignment
kusano 7d535a
      DO 95 J = 1,N
kusano 7d535a
        IF (JPERM(J).NE.0) GO TO 95
kusano 7d535a
        DO 50 K = IP(J),IP(J+1)-1
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          AI = ABS(A(K))
kusano 7d535a
          IF (AI.LT.BV) GO TO 50
kusano 7d535a
          IF (IPERM(I).EQ.0) GO TO 90
kusano 7d535a
          JJ = IPERM(I)
kusano 7d535a
          KK1 = PR(JJ)
kusano 7d535a
          KK2 = IP(JJ+1) - 1
kusano 7d535a
          IF (KK1.GT.KK2) GO TO 50
kusano 7d535a
          DO 70 KK = KK1,KK2
kusano 7d535a
            II = IRN(KK)
kusano 7d535a
            IF (IPERM(II).NE.0) GO TO 70
kusano 7d535a
            IF (ABS(A(KK)).GE.BV) GO TO 80
kusano 7d535a
   70     CONTINUE
kusano 7d535a
          PR(JJ) = KK2 + 1
kusano 7d535a
   50   CONTINUE
kusano 7d535a
        GO TO 95
kusano 7d535a
   80   JPERM(JJ) = II
kusano 7d535a
        IPERM(II) = JJ
kusano 7d535a
        PR(JJ) = KK + 1
kusano 7d535a
   90   NUM = NUM + 1
kusano 7d535a
        JPERM(J) = I
kusano 7d535a
        IPERM(I) = J
kusano 7d535a
        PR(J) = K + 1
kusano 7d535a
   95 CONTINUE
kusano 7d535a
      IF (NUM.EQ.N) GO TO 1000
kusano 7d535a
kusano 7d535a
C Prepare for main loop
kusano 7d535a
      DO 99 I = 1,N
kusano 7d535a
        D(I) = MINONE
kusano 7d535a
        L(I) = 0
kusano 7d535a
   99 CONTINUE
kusano 7d535a
kusano 7d535a
C Main loop ... each pass round this loop is similar to Dijkstra's 
kusano 7d535a
C algorithm for solving the single source shortest path problem 
kusano 7d535a
kusano 7d535a
      DO 100 JORD = 1,N
kusano 7d535a
kusano 7d535a
        IF (JPERM(JORD).NE.0) GO TO 100
kusano 7d535a
        QLEN = 0
kusano 7d535a
        LOW = N + 1
kusano 7d535a
        UP = N + 1
kusano 7d535a
C CSP is cost of shortest path to any unassigned row
kusano 7d535a
C ISP is matrix position of unassigned row element in shortest path
kusano 7d535a
C JSP is column index of unassigned row element in shortest path
kusano 7d535a
        CSP = MINONE
kusano 7d535a
C Build shortest path tree starting from unassigned column JORD
kusano 7d535a
        J = JORD
kusano 7d535a
        PR(J) = -1
kusano 7d535a
kusano 7d535a
C Scan column J
kusano 7d535a
        DO 115 K = IP(J),IP(J+1)-1
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          DNEW = ABS(A(K))
kusano 7d535a
          IF (CSP.GE.DNEW) GO TO 115
kusano 7d535a
          IF (IPERM(I).EQ.0) THEN
kusano 7d535a
C Row I is unassigned; update shortest path info
kusano 7d535a
            CSP = DNEW
kusano 7d535a
            ISP = I
kusano 7d535a
            JSP = J
kusano 7d535a
            IF (CSP.GE.BV) GO TO 160
kusano 7d535a
          ELSE
kusano 7d535a
            D(I) = DNEW
kusano 7d535a
            IF (DNEW.GE.BV) THEN
kusano 7d535a
C Add row I to Q2
kusano 7d535a
              LOW = LOW - 1
kusano 7d535a
              Q(LOW) = I
kusano 7d535a
            ELSE
kusano 7d535a
C Add row I to Q, and push it 
kusano 7d535a
              QLEN = QLEN + 1
kusano 7d535a
              L(I) = QLEN
kusano 7d535a
              CALL MC64DD(I,N,Q,D,L,1)
kusano 7d535a
            ENDIF
kusano 7d535a
            JJ = IPERM(I)
kusano 7d535a
            PR(JJ) = J
kusano 7d535a
          ENDIF
kusano 7d535a
  115   CONTINUE
kusano 7d535a
kusano 7d535a
        DO 150 JDUM = 1,NUM
kusano 7d535a
C If Q2 is empty, extract new rows from Q
kusano 7d535a
          IF (LOW.EQ.UP) THEN
kusano 7d535a
            IF (QLEN.EQ.0) GO TO 160
kusano 7d535a
            I = Q(1)
kusano 7d535a
            IF (CSP.GE.D(I)) GO TO 160
kusano 7d535a
            BV = D(I)
kusano 7d535a
            DO 152 IDUM = 1,N
kusano 7d535a
              CALL MC64ED(QLEN,N,Q,D,L,1)
kusano 7d535a
              L(I) = 0
kusano 7d535a
              LOW = LOW - 1
kusano 7d535a
              Q(LOW) = I
kusano 7d535a
              IF (QLEN.EQ.0) GO TO 153
kusano 7d535a
              I = Q(1)
kusano 7d535a
              IF (D(I).NE.BV) GO TO 153
kusano 7d535a
  152       CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
          ENDIF
kusano 7d535a
C Move row Q0 
kusano 7d535a
  153     UP = UP - 1
kusano 7d535a
          Q0 = Q(UP)
kusano 7d535a
          DQ0 = D(Q0)
kusano 7d535a
          L(Q0) = UP
kusano 7d535a
C Scan column that matches with row Q0
kusano 7d535a
          J = IPERM(Q0)
kusano 7d535a
          DO 155 K = IP(J),IP(J+1)-1
kusano 7d535a
            I = IRN(K)
kusano 7d535a
C Update D(I) 
kusano 7d535a
            IF (L(I).GE.UP) GO TO 155
kusano 7d535a
            DNEW = MIN(DQ0,ABS(A(K)))
kusano 7d535a
            IF (CSP.GE.DNEW) GO TO 155
kusano 7d535a
            IF (IPERM(I).EQ.0) THEN
kusano 7d535a
C Row I is unassigned; update shortest path info
kusano 7d535a
              CSP = DNEW
kusano 7d535a
              ISP = I
kusano 7d535a
              JSP = J
kusano 7d535a
              IF (CSP.GE.BV) GO TO 160
kusano 7d535a
            ELSE
kusano 7d535a
              DI = D(I)
kusano 7d535a
              IF (DI.GE.BV .OR. DI.GE.DNEW) GO TO 155
kusano 7d535a
              D(I) = DNEW
kusano 7d535a
              IF (DNEW.GE.BV) THEN
kusano 7d535a
C Delete row I from Q (if necessary); add row I to Q2
kusano 7d535a
                IF (DI.NE.MINONE)
kusano 7d535a
     *            CALL MC64FD(L(I),QLEN,N,Q,D,L,1)
kusano 7d535a
                L(I) = 0
kusano 7d535a
                LOW = LOW - 1
kusano 7d535a
                Q(LOW) = I
kusano 7d535a
              ELSE
kusano 7d535a
C Add row I to Q (if necessary); push row I up Q
kusano 7d535a
                IF (DI.EQ.MINONE) THEN
kusano 7d535a
                  QLEN = QLEN + 1
kusano 7d535a
                  L(I) = QLEN
kusano 7d535a
                ENDIF
kusano 7d535a
                CALL MC64DD(I,N,Q,D,L,1)
kusano 7d535a
              ENDIF
kusano 7d535a
C Update tree
kusano 7d535a
              JJ = IPERM(I)
kusano 7d535a
              PR(JJ) = J
kusano 7d535a
            ENDIF
kusano 7d535a
  155     CONTINUE
kusano 7d535a
  150   CONTINUE
kusano 7d535a
       
kusano 7d535a
C If CSP = MINONE, no augmenting path is found
kusano 7d535a
  160   IF (CSP.EQ.MINONE) GO TO 190
kusano 7d535a
C Update bottleneck value
kusano 7d535a
        BV = MIN(BV,CSP)
kusano 7d535a
C Find augmenting path by tracing backward in PR; update IPERM,JPERM
kusano 7d535a
        NUM = NUM + 1
kusano 7d535a
        I = ISP
kusano 7d535a
        J = JSP
kusano 7d535a
        DO 170 JDUM = 1,NUM+1
kusano 7d535a
          I0 = JPERM(J)
kusano 7d535a
          JPERM(J) = I
kusano 7d535a
          IPERM(I) = J
kusano 7d535a
          J = PR(J)
kusano 7d535a
          IF (J.EQ.-1) GO TO 190
kusano 7d535a
          I = I0
kusano 7d535a
  170   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
  190   DO 191 KK = UP,N
kusano 7d535a
          I = Q(KK)
kusano 7d535a
          D(I) = MINONE
kusano 7d535a
          L(I) = 0
kusano 7d535a
  191   CONTINUE 
kusano 7d535a
        DO 192 KK = LOW,UP-1
kusano 7d535a
          I = Q(KK)
kusano 7d535a
          D(I) = MINONE
kusano 7d535a
  192   CONTINUE
kusano 7d535a
        DO 193 KK = 1,QLEN
kusano 7d535a
          I = Q(KK)
kusano 7d535a
          D(I) = MINONE
kusano 7d535a
          L(I) = 0
kusano 7d535a
  193   CONTINUE
kusano 7d535a
kusano 7d535a
  100 CONTINUE
kusano 7d535a
C End of main loop
kusano 7d535a
kusano 7d535a
C BV is bottleneck value of final matching
kusano 7d535a
      IF (NUM.EQ.N) GO TO 1000
kusano 7d535a
kusano 7d535a
C Matrix is structurally singular, complete IPERM.
kusano 7d535a
C JPERM, PR are work arrays
kusano 7d535a
      DO 300 J = 1,N
kusano 7d535a
        JPERM(J) = 0
kusano 7d535a
  300 CONTINUE
kusano 7d535a
      K = 0
kusano 7d535a
      DO 310 I = 1,N
kusano 7d535a
        IF (IPERM(I).EQ.0) THEN
kusano 7d535a
          K = K + 1
kusano 7d535a
          PR(K) = I
kusano 7d535a
        ELSE
kusano 7d535a
          J = IPERM(I)
kusano 7d535a
          JPERM(J) = I
kusano 7d535a
        ENDIF
kusano 7d535a
  310 CONTINUE
kusano 7d535a
      K = 0
kusano 7d535a
      DO 320 I = 1,N
kusano 7d535a
        IF (JPERM(I).NE.0) GO TO 320
kusano 7d535a
        K = K + 1
kusano 7d535a
        JDUM = PR(K)
kusano 7d535a
        IPERM(JDUM) = I
kusano 7d535a
  320 CONTINUE
kusano 7d535a
kusano 7d535a
 1000 RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64DD(I,N,Q,D,L,IWAY)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER I,N,IWAY
kusano 7d535a
      INTEGER Q(N),L(N)
kusano 7d535a
      DOUBLE PRECISION D(N)
kusano 7d535a
kusano 7d535a
C Variables N,Q,D,L are described in MC64B/BD
kusano 7d535a
C IF IWAY is equal to 1, then
kusano 7d535a
C node I is pushed from its current position upwards
kusano 7d535a
C IF IWAY is not equal to 1, then
kusano 7d535a
C node I is pushed from its current position downwards
kusano 7d535a
kusano 7d535a
C Local variables and parameters
kusano 7d535a
      INTEGER IDUM,K,POS,POSK,QK
kusano 7d535a
      PARAMETER (K=2)
kusano 7d535a
      DOUBLE PRECISION DI
kusano 7d535a
kusano 7d535a
      DI = D(I)
kusano 7d535a
      POS = L(I)
kusano 7d535a
C POS is index of current position of I in the tree
kusano 7d535a
      IF (IWAY.EQ.1) THEN
kusano 7d535a
        DO 10 IDUM = 1,N
kusano 7d535a
          IF (POS.LE.1) GO TO 20
kusano 7d535a
          POSK = POS/K
kusano 7d535a
          QK = Q(POSK)
kusano 7d535a
          IF (DI.LE.D(QK)) GO TO 20 
kusano 7d535a
          Q(POS) = QK
kusano 7d535a
          L(QK) = POS 
kusano 7d535a
          POS = POSK
kusano 7d535a
   10   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
      ELSE
kusano 7d535a
        DO 15 IDUM = 1,N
kusano 7d535a
          IF (POS.LE.1) GO TO 20
kusano 7d535a
          POSK = POS/K
kusano 7d535a
          QK = Q(POSK)
kusano 7d535a
          IF (DI.GE.D(QK)) GO TO 20
kusano 7d535a
          Q(POS) = QK
kusano 7d535a
          L(QK) = POS
kusano 7d535a
          POS = POSK
kusano 7d535a
   15   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
      ENDIF
kusano 7d535a
C End of dummy if; this point is never reached
kusano 7d535a
   20 Q(POS) = I
kusano 7d535a
      L(I) = POS
kusano 7d535a
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
 
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64ED(QLEN,N,Q,D,L,IWAY)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER QLEN,N,IWAY
kusano 7d535a
      INTEGER Q(N),L(N)
kusano 7d535a
      DOUBLE PRECISION D(N)
kusano 7d535a
kusano 7d535a
C Variables QLEN,N,Q,D,L are described in MC64B/BD (IWAY = 1) or
kusano 7d535a
C     MC64W/WD (IWAY = 2)
kusano 7d535a
C The root node is deleted from the binary heap.
kusano 7d535a
kusano 7d535a
C Local variables and parameters
kusano 7d535a
      INTEGER I,IDUM,K,POS,POSK
kusano 7d535a
      PARAMETER (K=2)
kusano 7d535a
      DOUBLE PRECISION DK,DR,DI
kusano 7d535a
kusano 7d535a
C Move last element to begin of Q
kusano 7d535a
      I = Q(QLEN)
kusano 7d535a
      DI = D(I)
kusano 7d535a
      QLEN = QLEN - 1
kusano 7d535a
      POS = 1
kusano 7d535a
      IF (IWAY.EQ.1) THEN
kusano 7d535a
        DO 10 IDUM = 1,N
kusano 7d535a
          POSK = K*POS
kusano 7d535a
          IF (POSK.GT.QLEN) GO TO 20
kusano 7d535a
          DK = D(Q(POSK))
kusano 7d535a
          IF (POSK.LT.QLEN) THEN
kusano 7d535a
            DR = D(Q(POSK+1))
kusano 7d535a
            IF (DK.LT.DR) THEN
kusano 7d535a
              POSK = POSK + 1
kusano 7d535a
              DK = DR
kusano 7d535a
            ENDIF
kusano 7d535a
          ENDIF
kusano 7d535a
          IF (DI.GE.DK) GO TO 20
kusano 7d535a
C Exchange old last element with larger priority child
kusano 7d535a
          Q(POS) = Q(POSK)
kusano 7d535a
          L(Q(POS)) = POS
kusano 7d535a
          POS = POSK
kusano 7d535a
   10   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
      ELSE
kusano 7d535a
        DO 15 IDUM = 1,N
kusano 7d535a
          POSK = K*POS
kusano 7d535a
          IF (POSK.GT.QLEN) GO TO 20
kusano 7d535a
          DK = D(Q(POSK))
kusano 7d535a
          IF (POSK.LT.QLEN) THEN
kusano 7d535a
            DR = D(Q(POSK+1))
kusano 7d535a
            IF (DK.GT.DR) THEN
kusano 7d535a
              POSK = POSK + 1
kusano 7d535a
              DK = DR
kusano 7d535a
            ENDIF
kusano 7d535a
          ENDIF
kusano 7d535a
          IF (DI.LE.DK) GO TO 20
kusano 7d535a
C Exchange old last element with smaller child
kusano 7d535a
          Q(POS) = Q(POSK)
kusano 7d535a
          L(Q(POS)) = POS
kusano 7d535a
          POS = POSK
kusano 7d535a
   15   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
      ENDIF
kusano 7d535a
C End of dummy if; this point is never reached
kusano 7d535a
   20 Q(POS) = I
kusano 7d535a
      L(I) = POS
kusano 7d535a
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64FD(POS0,QLEN,N,Q,D,L,IWAY)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER POS0,QLEN,N,IWAY
kusano 7d535a
      INTEGER Q(N),L(N)
kusano 7d535a
      DOUBLE PRECISION D(N)
kusano 7d535a
kusano 7d535a
C Variables QLEN,N,Q,D,L are described in MC64B/BD (IWAY = 1) or
kusano 7d535a
C     MC64WD (IWAY = 2).
kusano 7d535a
C Move last element in the heap 
kusano 7d535a
kusano 7d535a
      INTEGER I,IDUM,K,POS,POSK,QK
kusano 7d535a
      PARAMETER (K=2)
kusano 7d535a
      DOUBLE PRECISION DK,DR,DI
kusano 7d535a
 
kusano 7d535a
C Quick return, if possible
kusano 7d535a
      IF (QLEN.EQ.POS0) THEN
kusano 7d535a
        QLEN = QLEN - 1
kusano 7d535a
        RETURN
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Move last element from queue Q to position POS0
kusano 7d535a
C POS is current position of node I in the tree
kusano 7d535a
      I = Q(QLEN)
kusano 7d535a
      DI = D(I)
kusano 7d535a
      QLEN = QLEN - 1
kusano 7d535a
      POS = POS0
kusano 7d535a
      IF (IWAY.EQ.1) THEN
kusano 7d535a
        DO 10 IDUM = 1,N
kusano 7d535a
          IF (POS.LE.1) GO TO 20
kusano 7d535a
          POSK = POS/K
kusano 7d535a
          QK = Q(POSK)
kusano 7d535a
          IF (DI.LE.D(QK)) GO TO 20 
kusano 7d535a
          Q(POS) = QK
kusano 7d535a
          L(QK) = POS 
kusano 7d535a
          POS = POSK
kusano 7d535a
   10   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
   20   Q(POS) = I
kusano 7d535a
        L(I) = POS
kusano 7d535a
        DO 30 IDUM = 1,N
kusano 7d535a
          POSK = K*POS
kusano 7d535a
          IF (POSK.GT.QLEN) GO TO 40
kusano 7d535a
          DK = D(Q(POSK))
kusano 7d535a
          IF (POSK.LT.QLEN) THEN
kusano 7d535a
            DR = D(Q(POSK+1))
kusano 7d535a
            IF (DK.LT.DR) THEN
kusano 7d535a
              POSK = POSK + 1
kusano 7d535a
              DK = DR
kusano 7d535a
            ENDIF
kusano 7d535a
          ENDIF
kusano 7d535a
          IF (DI.GE.DK) GO TO 40
kusano 7d535a
          QK = Q(POSK)
kusano 7d535a
          Q(POS) = QK
kusano 7d535a
          L(QK) = POS
kusano 7d535a
          POS = POSK
kusano 7d535a
   30   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
      ELSE
kusano 7d535a
        DO 32 IDUM = 1,N
kusano 7d535a
          IF (POS.LE.1) GO TO 34
kusano 7d535a
          POSK = POS/K
kusano 7d535a
          QK = Q(POSK)
kusano 7d535a
          IF (DI.GE.D(QK)) GO TO 34 
kusano 7d535a
          Q(POS) = QK
kusano 7d535a
          L(QK) = POS 
kusano 7d535a
          POS = POSK
kusano 7d535a
   32   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
   34   Q(POS) = I
kusano 7d535a
        L(I) = POS
kusano 7d535a
        DO 36 IDUM = 1,N
kusano 7d535a
          POSK = K*POS
kusano 7d535a
          IF (POSK.GT.QLEN) GO TO 40
kusano 7d535a
          DK = D(Q(POSK))
kusano 7d535a
          IF (POSK.LT.QLEN) THEN
kusano 7d535a
            DR = D(Q(POSK+1))
kusano 7d535a
            IF (DK.GT.DR) THEN
kusano 7d535a
              POSK = POSK + 1
kusano 7d535a
              DK = DR
kusano 7d535a
            ENDIF
kusano 7d535a
          ENDIF
kusano 7d535a
          IF (DI.LE.DK) GO TO 40
kusano 7d535a
          QK = Q(POSK)
kusano 7d535a
          Q(POS) = QK
kusano 7d535a
          L(QK) = POS
kusano 7d535a
          POS = POSK
kusano 7d535a
   36   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
      ENDIF
kusano 7d535a
C End of dummy if; this point is never reached
kusano 7d535a
   40 Q(POS) = I
kusano 7d535a
      L(I) = POS
kusano 7d535a
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64RD(N,NE,IP,IRN,A)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER N,NE
kusano 7d535a
      INTEGER IP(N+1),IRN(NE)
kusano 7d535a
      DOUBLE PRECISION A(NE)
kusano 7d535a
kusano 7d535a
C This subroutine sorts the entries in each column of the 
kusano 7d535a
C sparse matrix (defined by N,NE,IP,IRN,A) by decreasing
kusano 7d535a
C numerical value.
kusano 7d535a
kusano 7d535a
C Local constants
kusano 7d535a
      INTEGER THRESH,TDLEN
kusano 7d535a
      PARAMETER (THRESH=15,TDLEN=50)
kusano 7d535a
C Local variables
kusano 7d535a
      INTEGER J,IPJ,K,LEN,R,S,HI,FIRST,MID,LAST,TD
kusano 7d535a
      DOUBLE PRECISION HA,KEY
kusano 7d535a
C Local arrays
kusano 7d535a
      INTEGER TODO(TDLEN)
kusano 7d535a
      
kusano 7d535a
      DO 100 J = 1,N
kusano 7d535a
        LEN = IP(J+1) - IP(J)
kusano 7d535a
        IF (LEN.LE.1) GO TO 100
kusano 7d535a
        IPJ = IP(J)
kusano 7d535a
kusano 7d535a
C Sort array roughly with partial quicksort
kusano 7d535a
        IF (LEN.LT.THRESH) GO TO 400
kusano 7d535a
        TODO(1) = IPJ
kusano 7d535a
        TODO(2) = IPJ + LEN
kusano 7d535a
        TD = 2
kusano 7d535a
  500   CONTINUE
kusano 7d535a
        FIRST = TODO(TD-1)
kusano 7d535a
        LAST = TODO(TD)
kusano 7d535a
C KEY is the smallest of two values present in interval [FIRST,LAST)
kusano 7d535a
        KEY = A((FIRST+LAST)/2)
kusano 7d535a
        DO 475 K = FIRST,LAST-1
kusano 7d535a
          HA = A(K)
kusano 7d535a
          IF (HA.EQ.KEY) GO TO 475
kusano 7d535a
          IF (HA.GT.KEY) GO TO 470
kusano 7d535a
          KEY = HA
kusano 7d535a
          GO TO 470
kusano 7d535a
  475   CONTINUE
kusano 7d535a
C Only one value found in interval, so it is already sorted
kusano 7d535a
        TD = TD - 2
kusano 7d535a
        GO TO 425
kusano 7d535a
kusano 7d535a
C Reorder interval [FIRST,LAST) such that entries before MID are gt KEY
kusano 7d535a
  470   MID = FIRST
kusano 7d535a
        DO 450 K = FIRST,LAST-1
kusano 7d535a
          IF (A(K).LE.KEY) GO TO 450
kusano 7d535a
          HA = A(MID)
kusano 7d535a
          A(MID) = A(K)
kusano 7d535a
          A(K) = HA
kusano 7d535a
          HI = IRN(MID)
kusano 7d535a
          IRN(MID) = IRN(K)
kusano 7d535a
          IRN(K) = HI
kusano 7d535a
          MID = MID + 1
kusano 7d535a
  450   CONTINUE
kusano 7d535a
C Both subintervals [FIRST,MID), [MID,LAST) are nonempty
kusano 7d535a
C Stack the longest of the two subintervals first
kusano 7d535a
        IF (MID-FIRST.GE.LAST-MID) THEN
kusano 7d535a
          TODO(TD+2) = LAST
kusano 7d535a
          TODO(TD+1) = MID
kusano 7d535a
          TODO(TD) = MID
kusano 7d535a
C          TODO(TD-1) = FIRST
kusano 7d535a
        ELSE
kusano 7d535a
          TODO(TD+2) = MID
kusano 7d535a
          TODO(TD+1) = FIRST
kusano 7d535a
          TODO(TD) = LAST
kusano 7d535a
          TODO(TD-1) = MID
kusano 7d535a
        ENDIF
kusano 7d535a
        TD = TD + 2
kusano 7d535a
kusano 7d535a
  425   CONTINUE
kusano 7d535a
        IF (TD.EQ.0) GO TO 400 
kusano 7d535a
C There is still work to be done
kusano 7d535a
        IF (TODO(TD)-TODO(TD-1).GE.THRESH) GO TO 500
kusano 7d535a
C Next interval is already short enough for straightforward insertion
kusano 7d535a
        TD = TD - 2
kusano 7d535a
        GO TO 425
kusano 7d535a
kusano 7d535a
C Complete sorting with straightforward insertion
kusano 7d535a
  400   DO 200 R = IPJ+1,IPJ+LEN-1
kusano 7d535a
          IF (A(R-1) .LT. A(R)) THEN
kusano 7d535a
            HA = A(R)
kusano 7d535a
            HI = IRN(R)
kusano 7d535a
            A(R) = A(R-1)
kusano 7d535a
            IRN(R) = IRN(R-1)
kusano 7d535a
            DO 300 S = R-1,IPJ+1,-1
kusano 7d535a
              IF (A(S-1) .LT. HA) THEN
kusano 7d535a
                A(S) = A(S-1)
kusano 7d535a
                IRN(S) = IRN(S-1)
kusano 7d535a
              ELSE
kusano 7d535a
                A(S) = HA
kusano 7d535a
                IRN(S) = HI
kusano 7d535a
                GO TO 200 
kusano 7d535a
              END IF
kusano 7d535a
  300       CONTINUE
kusano 7d535a
            A(IPJ) = HA
kusano 7d535a
            IRN(IPJ) = HI
kusano 7d535a
          END IF
kusano 7d535a
  200   CONTINUE
kusano 7d535a
kusano 7d535a
  100 CONTINUE
kusano 7d535a
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64SD(N,NE,IP,IRN,A,IPERM,NUMX,
kusano 7d535a
     &           W,LEN,LENL,LENH,FC,IW,IW4)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER N,NE,NUMX
kusano 7d535a
      INTEGER IP(N+1),IRN(NE),IPERM(N), 
kusano 7d535a
     &        W(N),LEN(N),LENL(N),LENH(N),FC(N),IW(N),IW4(4*N)
kusano 7d535a
      DOUBLE PRECISION A(NE)
kusano 7d535a
kusano 7d535a
C N, NE, IP, IRN, are described in MC64A/AD.
kusano 7d535a
C A is a REAL (DOUBLE PRECISION in the D-version) array of length NE.
kusano 7d535a
C   A(K), K=1..NE, must be set to the value of the entry that 
kusano 7d535a
C   corresponds to IRN(k). The entries in each column must be 
kusano 7d535a
C   non-negative and ordered by decreasing value. 
kusano 7d535a
C IPERM is an INTEGER array of length N. On exit, it contains the 
kusano 7d535a
C   bottleneck matching: IPERM(I) - 0 or row I is matched to column 
kusano 7d535a
C   IPERM(I).
kusano 7d535a
C NUMX is an INTEGER variable. On exit, it contains the cardinality 
kusano 7d535a
C   of the matching stored in IPERM.
kusano 7d535a
C IW is an INTEGER work array of length 10N.
kusano 7d535a
kusano 7d535a
C FC is an integer array of length N that contains the list of 
kusano 7d535a
C   unmatched columns. 
kusano 7d535a
C LEN(J), LENL(J), LENH(J) are integer arrays of length N that point 
kusano 7d535a
C   to entries in matrix column J.
kusano 7d535a
C   In the matrix defined by the column parts IP(J)+LENL(J) we know 
kusano 7d535a
C   a matching does not exist; in the matrix defined by the column 
kusano 7d535a
C   parts IP(J)+LENH(J) we know one exists.
kusano 7d535a
C   LEN(J) lies between LENL(J) and LENH(J) and determines the matrix
kusano 7d535a
C   that is tested for a maximum matching.
kusano 7d535a
C W is an integer array of length N and contains the indices of the 
kusano 7d535a
C   columns for which LENL ne LENH.
kusano 7d535a
C WLEN is number of indices stored in array W.
kusano 7d535a
C IW is integer work array of length N.
kusano 7d535a
C IW4 is integer work array of length 4N used by MC64U/UD.
kusano 7d535a
kusano 7d535a
      INTEGER NUM,NVAL,WLEN,II,I,J,K,L,CNT,MOD,IDUM1,IDUM2,IDUM3
kusano 7d535a
      DOUBLE PRECISION BVAL,BMIN,BMAX,RINF
kusano 7d535a
c      EXTERNAL FD05AD,MC64QD,MC64UD
kusano 7d535a
c      DOUBLE PRECISION FD05AD
kusano 7d535a
      EXTERNAL MC64QD,MC64UD, DLAMCH
kusano 7d535a
      DOUBLE PRECISION DLAMCH
kusano 7d535a
kusano 7d535a
C BMIN and BMAX are such that a maximum matching exists for the input
kusano 7d535a
C   matrix in which all entries smaller than BMIN are dropped. 
kusano 7d535a
C   For BMAX, a maximum matching does not exist.
kusano 7d535a
C BVAL is a value between BMIN and BMAX.
kusano 7d535a
C CNT is the number of calls made to MC64U/UD so far.
kusano 7d535a
C NUM is the cardinality of last matching found.
kusano 7d535a
kusano 7d535a
C Set RINF to largest positive real number
kusano 7d535a
c XSL      RINF = FD05AD(5)
kusano 7d535a
      RINF = DLAMCH('Overflow')
kusano 7d535a
kusano 7d535a
C Compute a first maximum matching from scratch on whole matrix.
kusano 7d535a
      DO 20 J = 1,N
kusano 7d535a
        FC(J) = J
kusano 7d535a
        IW(J) = 0
kusano 7d535a
        LEN(J) = IP(J+1) - IP(J)
kusano 7d535a
   20 CONTINUE
kusano 7d535a
C The first call to MC64U/UD
kusano 7d535a
      CNT = 1
kusano 7d535a
      MOD = 1
kusano 7d535a
      NUMX = 0
kusano 7d535a
      CALL MC64UD(CNT,MOD,N,IRN,NE,IP,LEN,FC,IW,NUMX,N,
kusano 7d535a
     &            IW4(1),IW4(N+1),IW4(2*N+1),IW4(3*N+1))
kusano 7d535a
kusano 7d535a
C IW contains a maximum matching of length NUMX.
kusano 7d535a
      NUM = NUMX
kusano 7d535a
kusano 7d535a
      IF (NUM.NE.N) THEN
kusano 7d535a
C Matrix is structurally singular
kusano 7d535a
        BMAX = RINF
kusano 7d535a
      ELSE
kusano 7d535a
C Matrix is structurally nonsingular, NUM=NUMX=N;
kusano 7d535a
C Set BMAX just above the smallest of all the maximum absolute 
kusano 7d535a
C values of the columns
kusano 7d535a
        BMAX = RINF
kusano 7d535a
        DO 30 J = 1,N
kusano 7d535a
          BVAL = 0.0
kusano 7d535a
          DO 25 K = IP(J),IP(J+1)-1
kusano 7d535a
            IF (A(K).GT.BVAL) BVAL = A(K)
kusano 7d535a
   25     CONTINUE
kusano 7d535a
          IF (BVAL.LT.BMAX) BMAX = BVAL
kusano 7d535a
   30   CONTINUE
kusano 7d535a
        BMAX = 1.001 * BMAX
kusano 7d535a
      ENDIF
kusano 7d535a
kusano 7d535a
C Initialize BVAL,BMIN
kusano 7d535a
      BVAL = 0.0
kusano 7d535a
      BMIN = 0.0
kusano 7d535a
C Initialize LENL,LEN,LENH,W,WLEN according to BMAX.
kusano 7d535a
C Set LEN(J), LENH(J) just after last entry in column J. 
kusano 7d535a
C Set LENL(J) just after last entry in column J with value ge BMAX.
kusano 7d535a
      WLEN = 0
kusano 7d535a
      DO 48 J = 1,N
kusano 7d535a
        L = IP(J+1) - IP(J)
kusano 7d535a
        LENH(J) = L
kusano 7d535a
        LEN(J) = L
kusano 7d535a
        DO 45 K = IP(J),IP(J+1)-1
kusano 7d535a
          IF (A(K).LT.BMAX) GO TO 46
kusano 7d535a
   45   CONTINUE
kusano 7d535a
C Column J is empty or all entries are ge BMAX
kusano 7d535a
        K = IP(J+1)
kusano 7d535a
   46   LENL(J) = K - IP(J)
kusano 7d535a
C Add J to W if LENL(J) ne LENH(J)
kusano 7d535a
        IF (LENL(J).EQ.L) GO TO 48
kusano 7d535a
        WLEN = WLEN + 1
kusano 7d535a
        W(WLEN) = J
kusano 7d535a
   48 CONTINUE
kusano 7d535a
kusano 7d535a
C Main loop
kusano 7d535a
      DO 90 IDUM1 = 1,NE
kusano 7d535a
        IF (NUM.EQ.NUMX) THEN
kusano 7d535a
C We have a maximum matching in IW; store IW in IPERM
kusano 7d535a
          DO 50 I = 1,N
kusano 7d535a
            IPERM(I) = IW(I)
kusano 7d535a
   50     CONTINUE
kusano 7d535a
C Keep going round this loop until matching IW is no longer maximum.
kusano 7d535a
          DO 80 IDUM2 = 1,NE
kusano 7d535a
            BMIN = BVAL
kusano 7d535a
            IF (BMAX .EQ. BMIN) GO TO 99
kusano 7d535a
C Find splitting value BVAL
kusano 7d535a
            CALL MC64QD(IP,LENL,LEN,W,WLEN,A,NVAL,BVAL)
kusano 7d535a
            IF (NVAL.LE.1) GO TO 99 
kusano 7d535a
C Set LEN such that all matrix entries with value lt BVAL are 
kusano 7d535a
C discarded. Store old LEN in LENH. Do this for all columns W(K).
kusano 7d535a
C Each step, either K is incremented or WLEN is decremented.
kusano 7d535a
            K = 1
kusano 7d535a
            DO 70 IDUM3 = 1,N
kusano 7d535a
              IF (K.GT.WLEN) GO TO 71
kusano 7d535a
              J = W(K)
kusano 7d535a
              DO 55 II = IP(J)+LEN(J)-1,IP(J)+LENL(J),-1
kusano 7d535a
                IF (A(II).GE.BVAL) GO TO 60 
kusano 7d535a
                I = IRN(II)
kusano 7d535a
                IF (IW(I).NE.J) GO TO 55
kusano 7d535a
C Remove entry from matching
kusano 7d535a
                IW(I) = 0
kusano 7d535a
                NUM = NUM - 1
kusano 7d535a
                FC(N-NUM) = J
kusano 7d535a
   55         CONTINUE
kusano 7d535a
   60         LENH(J) = LEN(J)
kusano 7d535a
C IP(J)+LEN(J)-1 is last entry in column ge BVAL
kusano 7d535a
              LEN(J) = II - IP(J) + 1
kusano 7d535a
C If LENH(J) = LENL(J), remove J from W
kusano 7d535a
              IF (LENL(J).EQ.LENH(J)) THEN
kusano 7d535a
                W(K) = W(WLEN)
kusano 7d535a
                WLEN = WLEN - 1
kusano 7d535a
              ELSE
kusano 7d535a
                K = K + 1
kusano 7d535a
              ENDIF
kusano 7d535a
   70       CONTINUE
kusano 7d535a
   71       IF (NUM.LT.NUMX) GO TO 81
kusano 7d535a
   80     CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
C Set mode for next call to MC64U/UD
kusano 7d535a
   81     MOD = 1
kusano 7d535a
        ELSE
kusano 7d535a
C We do not have a maximum matching in IW. 
kusano 7d535a
          BMAX = BVAL
kusano 7d535a
C BMIN is the bottleneck value of a maximum matching;
kusano 7d535a
C for BMAX the matching is not maximum, so BMAX>BMIN
kusano 7d535a
C          IF (BMAX .EQ. BMIN) GO TO 99
kusano 7d535a
C Find splitting value BVAL
kusano 7d535a
          CALL MC64QD(IP,LEN,LENH,W,WLEN,A,NVAL,BVAL)
kusano 7d535a
          IF (NVAL.EQ.0. OR. BVAL.EQ.BMIN) GO TO 99 
kusano 7d535a
C Set LEN such that all matrix entries with value ge BVAL are
kusano 7d535a
C inside matrix. Store old LEN in LENL. Do this for all columns W(K).
kusano 7d535a
C Each step, either K is incremented or WLEN is decremented.
kusano 7d535a
          K = 1
kusano 7d535a
          DO 87 IDUM3 = 1,N
kusano 7d535a
            IF (K.GT.WLEN) GO TO 88
kusano 7d535a
            J = W(K)
kusano 7d535a
            DO 85 II = IP(J)+LEN(J),IP(J)+LENH(J)-1
kusano 7d535a
              IF (A(II).LT.BVAL) GO TO 86
kusano 7d535a
   85       CONTINUE
kusano 7d535a
   86       LENL(J) = LEN(J)
kusano 7d535a
            LEN(J) = II - IP(J)
kusano 7d535a
            IF (LENL(J).EQ.LENH(J)) THEN
kusano 7d535a
              W(K) = W(WLEN)
kusano 7d535a
              WLEN = WLEN - 1
kusano 7d535a
            ELSE
kusano 7d535a
              K = K + 1
kusano 7d535a
            ENDIF
kusano 7d535a
   87     CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
C Set mode for next call to MC64U/UD
kusano 7d535a
   88     MOD = 0
kusano 7d535a
        ENDIF
kusano 7d535a
        CNT = CNT + 1
kusano 7d535a
        CALL MC64UD(CNT,MOD,N,IRN,NE,IP,LEN,FC,IW,NUM,NUMX,
kusano 7d535a
     &            IW4(1),IW4(N+1),IW4(2*N+1),IW4(3*N+1))
kusano 7d535a
kusano 7d535a
C IW contains maximum matching of length NUM
kusano 7d535a
   90 CONTINUE 
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
kusano 7d535a
C BMIN is bottleneck value of final matching
kusano 7d535a
   99 IF (NUMX.EQ.N) GO TO 1000
kusano 7d535a
C The matrix is structurally singular, complete IPERM
kusano 7d535a
C W, IW are work arrays
kusano 7d535a
      DO 300 J = 1,N
kusano 7d535a
        W(J) = 0
kusano 7d535a
  300 CONTINUE
kusano 7d535a
      K = 0
kusano 7d535a
      DO 310 I = 1,N
kusano 7d535a
        IF (IPERM(I).EQ.0) THEN
kusano 7d535a
          K = K + 1
kusano 7d535a
          IW(K) = I
kusano 7d535a
        ELSE
kusano 7d535a
          J = IPERM(I)
kusano 7d535a
          W(J) = I
kusano 7d535a
        ENDIF
kusano 7d535a
  310 CONTINUE
kusano 7d535a
      K = 0
kusano 7d535a
      DO 320 J = 1,N
kusano 7d535a
        IF (W(J).NE.0) GO TO 320
kusano 7d535a
        K = K + 1
kusano 7d535a
        IDUM1 = IW(K)
kusano 7d535a
        IPERM(IDUM1) = J
kusano 7d535a
  320 CONTINUE
kusano 7d535a
kusano 7d535a
 1000 RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64QD(IP,LENL,LENH,W,WLEN,A,NVAL,VAL)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER WLEN,NVAL
kusano 7d535a
      INTEGER IP(*),LENL(*),LENH(*),W(*)
kusano 7d535a
      DOUBLE PRECISION A(*),VAL
kusano 7d535a
kusano 7d535a
C This routine searches for at most XX different numerical values 
kusano 7d535a
C in the columns W(1:WLEN). XX>=2. 
kusano 7d535a
C Each column J is scanned between IP(J)+LENL(J) and IP(J)+LENH(J)-1 
kusano 7d535a
C until XX values are found or all columns have been considered.
kusano 7d535a
C On output, NVAL is the number of different values that is found 
kusano 7d535a
C and SPLIT(1:NVAL) contains the values in decreasing order.
kusano 7d535a
C If NVAL > 0, the routine returns VAL = SPLIT((NVAL+1)/2). 
kusano 7d535a
C
kusano 7d535a
      INTEGER XX,J,K,II,S,POS
kusano 7d535a
      PARAMETER (XX=10)
kusano 7d535a
      DOUBLE PRECISION SPLIT(XX),HA
kusano 7d535a
kusano 7d535a
C Scan columns in W(1:WLEN). For each encountered value, if value not
kusano 7d535a
C already present in SPLIT(1:NVAL), insert value such that SPLIT 
kusano 7d535a
C remains sorted by decreasing value. 
kusano 7d535a
C The sorting is done by straightforward insertion; therefore the use
kusano 7d535a
C of this routine should be avoided for large XX (XX < 20).
kusano 7d535a
      NVAL = 0 
kusano 7d535a
      DO 10 K = 1,WLEN
kusano 7d535a
        J = W(K)
kusano 7d535a
        DO 15 II = IP(J)+LENL(J),IP(J)+LENH(J)-1
kusano 7d535a
          HA = A(II)
kusano 7d535a
          IF (NVAL.EQ.0) THEN
kusano 7d535a
            SPLIT(1) = HA
kusano 7d535a
            NVAL = 1
kusano 7d535a
          ELSE
kusano 7d535a
C Check presence of HA in SPLIT
kusano 7d535a
            DO 20 S = NVAL,1,-1
kusano 7d535a
              IF (SPLIT(S).EQ.HA) GO TO 15
kusano 7d535a
              IF (SPLIT(S).GT.HA) THEN
kusano 7d535a
                POS = S + 1
kusano 7d535a
                GO TO 21
kusano 7d535a
              ENDIF
kusano 7d535a
  20        CONTINUE
kusano 7d535a
            POS = 1
kusano 7d535a
C The insertion 
kusano 7d535a
  21        DO 22 S = NVAL,POS,-1
kusano 7d535a
              SPLIT(S+1) = SPLIT(S)
kusano 7d535a
  22        CONTINUE
kusano 7d535a
            SPLIT(POS) = HA
kusano 7d535a
            NVAL = NVAL + 1
kusano 7d535a
          ENDIF
kusano 7d535a
C Exit loop if XX values are found
kusano 7d535a
          IF (NVAL.EQ.XX) GO TO 11
kusano 7d535a
  15    CONTINUE
kusano 7d535a
  10  CONTINUE
kusano 7d535a
C Determine VAL
kusano 7d535a
  11  IF (NVAL.GT.0) VAL = SPLIT((NVAL+1)/2)
kusano 7d535a
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
      
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64UD(ID,MOD,N,IRN,LIRN,IP,LENC,FC,IPERM,NUM,NUMX,
kusano 7d535a
     &           PR,ARP,CV,OUT)
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER ID,MOD,N,LIRN,NUM,NUMX
kusano 7d535a
      INTEGER ARP(N),CV(N),IRN(LIRN),IP(N),
kusano 7d535a
     &        FC(N),IPERM(N),LENC(N),OUT(N),PR(N)
kusano 7d535a
kusano 7d535a
C PR(J) is the previous column to J in the depth first search.
kusano 7d535a
C   Array PR is used as workspace in the sorting algorithm.
kusano 7d535a
C Elements (I,IPERM(I)) I=1,..,N are entries at the end of the
kusano 7d535a
C   algorithm unless N assignments have not been made in which case
kusano 7d535a
C   N-NUM pairs (I,IPERM(I)) will not be entries in the matrix.
kusano 7d535a
C CV(I) is the most recent loop number (ID+JORD) at which row I
kusano 7d535a
C   was visited.
kusano 7d535a
C ARP(J) is the number of entries in column J which have been scanned 
kusano 7d535a
C   when looking for a cheap assignment.
kusano 7d535a
C OUT(J) is one less than the number of entries in column J which have 
kusano 7d535a
C   not been scanned during one pass through the main loop.
kusano 7d535a
C NUMX is maximum possible size of matching.
kusano 7d535a
kusano 7d535a
      INTEGER I,II,IN1,IN2,J,J1,JORD,K,KK,LAST,NFC,
kusano 7d535a
     &        NUM0,NUM1,NUM2,ID0,ID1
kusano 7d535a
kusano 7d535a
      IF (ID.EQ.1) THEN
kusano 7d535a
C The first call to MC64U/UD.
kusano 7d535a
C Initialize CV and ARP; parameters MOD, NUMX are not accessed
kusano 7d535a
        DO 5 I = 1,N
kusano 7d535a
          CV(I) = 0
kusano 7d535a
          ARP(I) = 0
kusano 7d535a
    5   CONTINUE
kusano 7d535a
        NUM1 = N
kusano 7d535a
        NUM2 = N
kusano 7d535a
      ELSE
kusano 7d535a
C Not the first call to MC64U/UD.
kusano 7d535a
C Re-initialize ARP if entries were deleted since last call to MC64U/UD
kusano 7d535a
        IF (MOD.EQ.1) THEN
kusano 7d535a
          DO 8 I = 1,N
kusano 7d535a
            ARP(I) = 0
kusano 7d535a
    8     CONTINUE
kusano 7d535a
        ENDIF
kusano 7d535a
        NUM1 = NUMX
kusano 7d535a
        NUM2 = N - NUMX
kusano 7d535a
      ENDIF
kusano 7d535a
      NUM0 = NUM
kusano 7d535a
kusano 7d535a
C NUM0 is size of input matching
kusano 7d535a
C NUM1 is maximum possible size of matching
kusano 7d535a
C NUM2 is maximum allowed number of unassigned rows/columns
kusano 7d535a
C NUM is size of current matching
kusano 7d535a
kusano 7d535a
C Quick return if possible
kusano 7d535a
C      IF (NUM.EQ.N) GO TO 199
kusano 7d535a
C NFC is number of rows/columns that could not be assigned
kusano 7d535a
      NFC = 0
kusano 7d535a
C Integers ID0+1 to ID0+N are unique numbers for call ID to MC64U/UD,
kusano 7d535a
C so 1st call uses 1..N, 2nd call uses N+1..2N, etc
kusano 7d535a
      ID0 = (ID-1)*N 
kusano 7d535a
kusano 7d535a
C Main loop. Each pass round this loop either results in a new
kusano 7d535a
C assignment or gives a column with no assignment
kusano 7d535a
kusano 7d535a
      DO 100 JORD = NUM0+1,N
kusano 7d535a
kusano 7d535a
C Each pass uses unique number ID1
kusano 7d535a
        ID1 = ID0 + JORD
kusano 7d535a
C J is unmatched column
kusano 7d535a
        J = FC(JORD-NUM0)
kusano 7d535a
        PR(J) = -1
kusano 7d535a
        DO 70 K = 1,JORD
kusano 7d535a
C Look for a cheap assignment
kusano 7d535a
          IF (ARP(J).GE.LENC(J)) GO TO 30
kusano 7d535a
          IN1 = IP(J) + ARP(J)
kusano 7d535a
          IN2 = IP(J) + LENC(J) - 1
kusano 7d535a
          DO 20 II = IN1,IN2
kusano 7d535a
            I = IRN(II)
kusano 7d535a
            IF (IPERM(I).EQ.0) GO TO 80
kusano 7d535a
   20     CONTINUE
kusano 7d535a
C No cheap assignment in row
kusano 7d535a
          ARP(J) = LENC(J)
kusano 7d535a
C Begin looking for assignment chain starting with row J
kusano 7d535a
   30     OUT(J) = LENC(J) - 1
kusano 7d535a
C Inner loop.  Extends chain by one or backtracks
kusano 7d535a
          DO 60 KK = 1,JORD
kusano 7d535a
            IN1 = OUT(J)
kusano 7d535a
            IF (IN1.LT.0) GO TO 50
kusano 7d535a
            IN2 = IP(J) + LENC(J) - 1
kusano 7d535a
            IN1 = IN2 - IN1
kusano 7d535a
C Forward scan
kusano 7d535a
            DO 40 II = IN1,IN2
kusano 7d535a
              I = IRN(II)
kusano 7d535a
              IF (CV(I).EQ.ID1) GO TO 40
kusano 7d535a
C Column J has not yet been accessed during this pass
kusano 7d535a
              J1 = J
kusano 7d535a
              J = IPERM(I)
kusano 7d535a
              CV(I) = ID1
kusano 7d535a
              PR(J) = J1
kusano 7d535a
              OUT(J1) = IN2 - II - 1
kusano 7d535a
              GO TO 70
kusano 7d535a
   40       CONTINUE
kusano 7d535a
C Backtracking step.
kusano 7d535a
   50       J1 = PR(J)
kusano 7d535a
            IF (J1.EQ.-1) THEN
kusano 7d535a
C No augmenting path exists for column J.
kusano 7d535a
              NFC = NFC + 1
kusano 7d535a
              FC(NFC) = J
kusano 7d535a
              IF (NFC.GT.NUM2) THEN
kusano 7d535a
C A matching of maximum size NUM1 is not possible
kusano 7d535a
                LAST = JORD
kusano 7d535a
                GO TO 101
kusano 7d535a
              ENDIF
kusano 7d535a
              GO TO 100
kusano 7d535a
            ENDIF
kusano 7d535a
            J = J1
kusano 7d535a
   60     CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
   70   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
kusano 7d535a
C New assignment is made.
kusano 7d535a
   80   IPERM(I) = J
kusano 7d535a
        ARP(J) = II - IP(J) + 1
kusano 7d535a
        NUM = NUM + 1
kusano 7d535a
        DO 90 K = 1,JORD
kusano 7d535a
          J = PR(J)
kusano 7d535a
          IF (J.EQ.-1) GO TO 95
kusano 7d535a
          II = IP(J) + LENC(J) - OUT(J) - 2
kusano 7d535a
          I = IRN(II)
kusano 7d535a
          IPERM(I) = J
kusano 7d535a
   90   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
kusano 7d535a
   95   IF (NUM.EQ.NUM1) THEN
kusano 7d535a
C A matching of maximum size NUM1 is found
kusano 7d535a
          LAST = JORD
kusano 7d535a
          GO TO 101
kusano 7d535a
        ENDIF
kusano 7d535a
C
kusano 7d535a
  100 CONTINUE
kusano 7d535a
kusano 7d535a
C All unassigned columns have been considered
kusano 7d535a
      LAST = N
kusano 7d535a
kusano 7d535a
C Now, a transversal is computed or is not possible.
kusano 7d535a
C Complete FC before returning.
kusano 7d535a
  101 DO 110 JORD = LAST+1,N
kusano 7d535a
        NFC = NFC + 1
kusano 7d535a
        FC(NFC) = FC(JORD-NUM0)
kusano 7d535a
  110 CONTINUE
kusano 7d535a
kusano 7d535a
C  199 RETURN
kusano 7d535a
      RETURN
kusano 7d535a
      END
kusano 7d535a
kusano 7d535a
C**********************************************************************
kusano 7d535a
      SUBROUTINE MC64WD(N,NE,IP,IRN,A,IPERM,NUM,
kusano 7d535a
     &           JPERM,OUT,PR,Q,L,U,D) 
kusano 7d535a
      IMPLICIT NONE
kusano 7d535a
C
kusano 7d535a
C *** Copyright (c) 1999  Council for the Central Laboratory of the
kusano 7d535a
C     Research Councils                                             ***
kusano 7d535a
C *** Although every effort has been made to ensure robustness and  ***
kusano 7d535a
C *** reliability of the subroutines in this MC64 suite, we         ***
kusano 7d535a
C *** disclaim any liability arising through the use or misuse of   ***
kusano 7d535a
C *** any of the subroutines.                                       ***
kusano 7d535a
C *** Any problems?   Contact ...
kusano 7d535a
C     Iain Duff (I.Duff@rl.ac.uk) or Jacko Koster (jak@ii.uib.no)   ***
kusano 7d535a
C
kusano 7d535a
      INTEGER N,NE,NUM
kusano 7d535a
      INTEGER IP(N+1),IRN(NE),IPERM(N),
kusano 7d535a
     &        JPERM(N),OUT(N),PR(N),Q(N),L(N)
kusano 7d535a
      DOUBLE PRECISION A(NE),U(N),D(N)
kusano 7d535a
kusano 7d535a
C N, NE, IP, IRN are described in MC64A/AD.
kusano 7d535a
C A is a REAL (DOUBLE PRECISION in the D-version) array of length NE.
kusano 7d535a
C   A(K), K=1..NE, must be set to the value of the entry that
kusano 7d535a
C   corresponds to IRN(K). It is not altered.
kusano 7d535a
C   All values A(K) must be non-negative.
kusano 7d535a
C IPERM is an INTEGER array of length N. On exit, it contains the 
kusano 7d535a
C   weighted matching: IPERM(I) = 0 or row I is matched to column 
kusano 7d535a
C   IPERM(I). 
kusano 7d535a
C NUM is an INTEGER variable. On exit, it contains the cardinality of 
kusano 7d535a
C   the matching stored in IPERM.
kusano 7d535a
C IW is an INTEGER work array of length 5N.
kusano 7d535a
C DW is a REAL (DOUBLE PRECISION in the D-version) array of length 2N.
kusano 7d535a
C   On exit, U = D(1:N) contains the dual row variable and 
kusano 7d535a
C   V = D(N+1:2N) contains the dual column variable. If the matrix 
kusano 7d535a
C   is structurally nonsingular (NUM = N), the following holds:
kusano 7d535a
C      U(I)+V(J) <= A(I,J)  if IPERM(I) |= J
kusano 7d535a
C      U(I)+V(J)  = A(I,J)  if IPERM(I)  = J
kusano 7d535a
C      U(I) = 0  if IPERM(I) = 0
kusano 7d535a
C      V(J) = 0  if there is no I for which IPERM(I) = J
kusano 7d535a
kusano 7d535a
C Local variables
kusano 7d535a
      INTEGER I,I0,II,J,JJ,JORD,Q0,QLEN,JDUM,ISP,JSP,
kusano 7d535a
     &        K,K0,K1,K2,KK,KK1,KK2,UP,LOW
kusano 7d535a
      DOUBLE PRECISION CSP,DI,DMIN,DNEW,DQ0,VJ
kusano 7d535a
C Local parameters
kusano 7d535a
      DOUBLE PRECISION RINF,ZERO
kusano 7d535a
      PARAMETER (ZERO=0.0D+0)
kusano 7d535a
C External subroutines and/or functions
kusano 7d535a
c      EXTERNAL FD05AD,MC64DD,MC64ED,MC64FD
kusano 7d535a
c      DOUBLE PRECISION FD05AD
kusano 7d535a
      EXTERNAL MC64DD,MC64ED,MC64FD, DLAMCH
kusano 7d535a
      DOUBLE PRECISION DLAMCH
kusano 7d535a
kusano 7d535a
kusano 7d535a
C Set RINF to largest positive real number
kusano 7d535a
c XSL      RINF = FD05AD(5)
kusano 7d535a
      RINF = DLAMCH('Overflow')
kusano 7d535a
kusano 7d535a
C Initialization
kusano 7d535a
      NUM = 0
kusano 7d535a
      DO 10 K = 1,N
kusano 7d535a
        U(K) = RINF
kusano 7d535a
        D(K) = ZERO
kusano 7d535a
        IPERM(K) = 0
kusano 7d535a
        JPERM(K) = 0
kusano 7d535a
        PR(K) = IP(K)
kusano 7d535a
        L(K) = 0
kusano 7d535a
   10 CONTINUE
kusano 7d535a
C Initialize U(I) 
kusano 7d535a
      DO 30 J = 1,N
kusano 7d535a
        DO 20 K = IP(J),IP(J+1)-1
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          IF (A(K).GT.U(I)) GO TO 20
kusano 7d535a
          U(I) = A(K)
kusano 7d535a
          IPERM(I) = J
kusano 7d535a
          L(I) = K
kusano 7d535a
   20   CONTINUE
kusano 7d535a
   30 CONTINUE
kusano 7d535a
      DO 40 I = 1,N
kusano 7d535a
        J = IPERM(I)
kusano 7d535a
        IF (J.EQ.0) GO TO 40
kusano 7d535a
C Row I is not empty
kusano 7d535a
        IPERM(I) = 0
kusano 7d535a
        IF (JPERM(J).NE.0) GO TO 40
kusano 7d535a
C Assignment of column J to row I
kusano 7d535a
        NUM = NUM + 1
kusano 7d535a
        IPERM(I) = J
kusano 7d535a
        JPERM(J) = L(I)
kusano 7d535a
   40 CONTINUE
kusano 7d535a
      IF (NUM.EQ.N) GO TO 1000
kusano 7d535a
C Scan unassigned columns; improve assignment
kusano 7d535a
      DO 95 J = 1,N
kusano 7d535a
C JPERM(J) ne 0 iff column J is already assigned
kusano 7d535a
        IF (JPERM(J).NE.0) GO TO 95
kusano 7d535a
        K1 = IP(J)
kusano 7d535a
        K2 = IP(J+1) - 1
kusano 7d535a
C Continue only if column J is not empty
kusano 7d535a
        IF (K1.GT.K2) GO TO 95
kusano 7d535a
        VJ = RINF
kusano 7d535a
        DO 50 K = K1,K2
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          DI = A(K) - U(I)
kusano 7d535a
          IF (DI.GT.VJ) GO TO 50
kusano 7d535a
          IF (DI.LT.VJ .OR. DI.EQ.RINF) GO TO 55
kusano 7d535a
          IF (IPERM(I).NE.0 .OR. IPERM(I0).EQ.0) GO TO 50
kusano 7d535a
   55     VJ = DI
kusano 7d535a
          I0 = I
kusano 7d535a
          K0 = K
kusano 7d535a
   50   CONTINUE
kusano 7d535a
        D(J) = VJ
kusano 7d535a
        K = K0
kusano 7d535a
        I = I0
kusano 7d535a
        IF (IPERM(I).EQ.0) GO TO 90
kusano 7d535a
        DO 60 K = K0,K2
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          IF (A(K)-U(I).GT.VJ) GO TO 60 
kusano 7d535a
          JJ = IPERM(I)
kusano 7d535a
C Scan remaining part of assigned column JJ 
kusano 7d535a
          KK1 = PR(JJ)
kusano 7d535a
          KK2 = IP(JJ+1) - 1
kusano 7d535a
          IF (KK1.GT.KK2) GO TO 60
kusano 7d535a
          DO 70 KK = KK1,KK2
kusano 7d535a
            II = IRN(KK)
kusano 7d535a
            IF (IPERM(II).GT.0) GO TO 70
kusano 7d535a
            IF (A(KK)-U(II).LE.D(JJ)) GO TO 80
kusano 7d535a
   70     CONTINUE
kusano 7d535a
          PR(JJ) = KK2 + 1
kusano 7d535a
   60   CONTINUE
kusano 7d535a
        GO TO 95
kusano 7d535a
   80   JPERM(JJ) = KK
kusano 7d535a
        IPERM(II) = JJ
kusano 7d535a
        PR(JJ) = KK + 1
kusano 7d535a
   90   NUM = NUM + 1
kusano 7d535a
        JPERM(J) = K
kusano 7d535a
        IPERM(I) = J
kusano 7d535a
        PR(J) = K + 1
kusano 7d535a
   95 CONTINUE
kusano 7d535a
      IF (NUM.EQ.N) GO TO 1000
kusano 7d535a
  
kusano 7d535a
C Prepare for main loop
kusano 7d535a
      DO 99 I = 1,N
kusano 7d535a
        D(I) = RINF
kusano 7d535a
        L(I) = 0
kusano 7d535a
   99 CONTINUE
kusano 7d535a
kusano 7d535a
C Main loop ... each pass round this loop is similar to Dijkstra's 
kusano 7d535a
C algorithm for solving the single source shortest path problem 
kusano 7d535a
kusano 7d535a
      DO 100 JORD = 1,N
kusano 7d535a
kusano 7d535a
        IF (JPERM(JORD).NE.0) GO TO 100
kusano 7d535a
C JORD is next unmatched column
kusano 7d535a
C DMIN is the length of shortest path in the tree
kusano 7d535a
        DMIN = RINF
kusano 7d535a
        QLEN = 0
kusano 7d535a
        LOW = N + 1
kusano 7d535a
        UP = N + 1
kusano 7d535a
C CSP is the cost of the shortest augmenting path to unassigned row
kusano 7d535a
C IRN(ISP). The corresponding column index is JSP.
kusano 7d535a
        CSP = RINF
kusano 7d535a
C Build shortest path tree starting from unassigned column (root) JORD
kusano 7d535a
        J = JORD
kusano 7d535a
        PR(J) = -1
kusano 7d535a
kusano 7d535a
C Scan column J
kusano 7d535a
        DO 115 K = IP(J),IP(J+1)-1
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          DNEW = A(K) - U(I)
kusano 7d535a
          IF (DNEW.GE.CSP) GO TO 115
kusano 7d535a
          IF (IPERM(I).EQ.0) THEN
kusano 7d535a
            CSP = DNEW
kusano 7d535a
            ISP = K
kusano 7d535a
            JSP = J
kusano 7d535a
          ELSE
kusano 7d535a
            IF (DNEW.LT.DMIN) DMIN = DNEW
kusano 7d535a
            D(I) = DNEW
kusano 7d535a
            QLEN = QLEN + 1
kusano 7d535a
            Q(QLEN) = K
kusano 7d535a
          ENDIF
kusano 7d535a
  115   CONTINUE
kusano 7d535a
C Initialize heap Q and Q2 with rows held in Q(1:QLEN)
kusano 7d535a
        Q0 = QLEN
kusano 7d535a
        QLEN = 0
kusano 7d535a
        DO 120 KK = 1,Q0
kusano 7d535a
          K = Q(KK)
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          IF (CSP.LE.D(I)) THEN
kusano 7d535a
            D(I) = RINF
kusano 7d535a
            GO TO 120
kusano 7d535a
          ENDIF
kusano 7d535a
          IF (D(I).LE.DMIN) THEN
kusano 7d535a
            LOW = LOW - 1
kusano 7d535a
            Q(LOW) = I
kusano 7d535a
            L(I) = LOW
kusano 7d535a
          ELSE
kusano 7d535a
            QLEN = QLEN + 1
kusano 7d535a
            L(I) = QLEN
kusano 7d535a
            CALL MC64DD(I,N,Q,D,L,2)
kusano 7d535a
          ENDIF
kusano 7d535a
C Update tree
kusano 7d535a
          JJ = IPERM(I)
kusano 7d535a
          OUT(JJ) = K
kusano 7d535a
          PR(JJ) = J
kusano 7d535a
  120   CONTINUE
kusano 7d535a
kusano 7d535a
        DO 150 JDUM = 1,NUM
kusano 7d535a
kusano 7d535a
C If Q2 is empty, extract rows from Q 
kusano 7d535a
          IF (LOW.EQ.UP) THEN
kusano 7d535a
            IF (QLEN.EQ.0) GO TO 160
kusano 7d535a
            I = Q(1)
kusano 7d535a
            IF (D(I).GE.CSP) GO TO 160
kusano 7d535a
            DMIN = D(I)
kusano 7d535a
  152       CALL MC64ED(QLEN,N,Q,D,L,2)
kusano 7d535a
            LOW = LOW - 1
kusano 7d535a
            Q(LOW) = I
kusano 7d535a
            L(I) = LOW
kusano 7d535a
            IF (QLEN.EQ.0) GO TO 153
kusano 7d535a
            I = Q(1)
kusano 7d535a
            IF (D(I).GT.DMIN) GO TO 153
kusano 7d535a
            GO TO 152
kusano 7d535a
          ENDIF
kusano 7d535a
C Q0 is row whose distance D(Q0) to the root is smallest
kusano 7d535a
  153     Q0 = Q(UP-1)
kusano 7d535a
          DQ0 = D(Q0)
kusano 7d535a
C Exit loop if path to Q0 is longer than the shortest augmenting path 
kusano 7d535a
          IF (DQ0.GE.CSP) GO TO 160
kusano 7d535a
          UP = UP - 1
kusano 7d535a
kusano 7d535a
C Scan column that matches with row Q0
kusano 7d535a
          J = IPERM(Q0)
kusano 7d535a
          VJ = DQ0 - A(JPERM(J)) + U(Q0)
kusano 7d535a
          DO 155 K = IP(J),IP(J+1)-1
kusano 7d535a
            I = IRN(K)
kusano 7d535a
            IF (L(I).GE.UP) GO TO 155
kusano 7d535a
C DNEW is new cost
kusano 7d535a
            DNEW = VJ + A(K)-U(I)
kusano 7d535a
C Do not update D(I) if DNEW ge cost of shortest path
kusano 7d535a
            IF (DNEW.GE.CSP) GO TO 155
kusano 7d535a
            IF (IPERM(I).EQ.0) THEN
kusano 7d535a
C Row I is unmatched; update shortest path info
kusano 7d535a
              CSP = DNEW
kusano 7d535a
              ISP = K
kusano 7d535a
              JSP = J
kusano 7d535a
            ELSE
kusano 7d535a
C Row I is matched; do not update D(I) if DNEW is larger
kusano 7d535a
              DI = D(I)
kusano 7d535a
              IF (DI.LE.DNEW) GO TO 155
kusano 7d535a
              IF (L(I).GE.LOW) GO TO 155
kusano 7d535a
              D(I) = DNEW
kusano 7d535a
              IF (DNEW.LE.DMIN) THEN
kusano 7d535a
                IF (L(I).NE.0) 
kusano 7d535a
     *            CALL MC64FD(L(I),QLEN,N,Q,D,L,2)
kusano 7d535a
                LOW = LOW - 1
kusano 7d535a
                Q(LOW) = I
kusano 7d535a
                L(I) = LOW
kusano 7d535a
              ELSE   
kusano 7d535a
                IF (L(I).EQ.0) THEN
kusano 7d535a
                  QLEN = QLEN + 1
kusano 7d535a
                  L(I) = QLEN
kusano 7d535a
                ENDIF
kusano 7d535a
                CALL MC64DD(I,N,Q,D,L,2)
kusano 7d535a
              ENDIF
kusano 7d535a
C Update tree
kusano 7d535a
              JJ = IPERM(I)
kusano 7d535a
              OUT(JJ) = K
kusano 7d535a
              PR(JJ) = J
kusano 7d535a
            ENDIF
kusano 7d535a
  155     CONTINUE
kusano 7d535a
  150   CONTINUE
kusano 7d535a
       
kusano 7d535a
C If CSP = RINF, no augmenting path is found
kusano 7d535a
  160   IF (CSP.EQ.RINF) GO TO 190
kusano 7d535a
C Find augmenting path by tracing backward in PR; update IPERM,JPERM
kusano 7d535a
        NUM = NUM + 1
kusano 7d535a
        I = IRN(ISP)
kusano 7d535a
        IPERM(I) = JSP
kusano 7d535a
        JPERM(JSP) = ISP
kusano 7d535a
        J = JSP
kusano 7d535a
        DO 170 JDUM = 1,NUM
kusano 7d535a
          JJ = PR(J) 
kusano 7d535a
          IF (JJ.EQ.-1) GO TO 180
kusano 7d535a
          K = OUT(J)
kusano 7d535a
          I = IRN(K)
kusano 7d535a
          IPERM(I) = JJ
kusano 7d535a
          JPERM(JJ) = K
kusano 7d535a
          J = JJ
kusano 7d535a
  170   CONTINUE
kusano 7d535a
C End of dummy loop; this point is never reached
kusano 7d535a
kusano 7d535a
C Update U for rows in Q(UP:N)
kusano 7d535a
  180   DO 185 KK = UP,N
kusano 7d535a
          I = Q(KK)
kusano 7d535a
          U(I) = U(I) + D(I) - CSP
kusano 7d535a
  185   CONTINUE 
kusano 7d535a
  190   DO 191 KK = LOW,N
kusano 7d535a
          I = Q(KK)
kusano 7d535a
          D(I) = RINF
kusano 7d535a
          L(I) = 0
kusano 7d535a
  191   CONTINUE 
kusano 7d535a
        DO 193 KK = 1,QLEN
kusano 7d535a
          I = Q(KK)
kusano 7d535a
          D(I) = RINF
kusano 7d535a
          L(I) = 0
kusano 7d535a
  193   CONTINUE
kusano 7d535a
kusano 7d535a
  100 CONTINUE
kusano 7d535a
C End of main loop
kusano 7d535a
kusano 7d535a
kusano 7d535a
C Set dual column variable in D(1:N)
kusano 7d535a
 1000 DO 200 J = 1,N
kusano 7d535a
        K = JPERM(J)
kusano 7d535a
        IF (K.NE.0) THEN
kusano 7d535a
          D(J) = A(K) - U(IRN(K))
kusano 7d535a
        ELSE
kusano 7d535a
          D(J) = ZERO
kusano 7d535a
        ENDIF
kusano 7d535a
        IF (IPERM(J).EQ.0) U(J) = ZERO
kusano 7d535a
  200 CONTINUE
kusano 7d535a
kusano 7d535a
      IF (NUM.EQ.N) GO TO 1100
kusano 7d535a
kusano 7d535a
C The matrix is structurally singular, complete IPERM.
kusano 7d535a
C JPERM, OUT are work arrays
kusano 7d535a
      DO 300 J = 1,N
kusano 7d535a
        JPERM(J) = 0
kusano 7d535a
  300 CONTINUE
kusano 7d535a
      K = 0
kusano 7d535a
      DO 310 I = 1,N
kusano 7d535a
        IF (IPERM(I).EQ.0) THEN
kusano 7d535a
          K = K + 1
kusano 7d535a
          OUT(K) = I
kusano 7d535a
        ELSE
kusano 7d535a
          J = IPERM(I)
kusano 7d535a
          JPERM(J) = I
kusano 7d535a
        ENDIF
kusano 7d535a
  310 CONTINUE
kusano 7d535a
      K = 0
kusano 7d535a
      DO 320 J = 1,N
kusano 7d535a
        IF (JPERM(J).NE.0) GO TO 320
kusano 7d535a
        K = K + 1
kusano 7d535a
        JDUM = OUT(K)
kusano 7d535a
        IPERM(JDUM) = J
kusano 7d535a
  320 CONTINUE
kusano 7d535a
 1100 RETURN
kusano 7d535a
      END
kusano 7d535a