      PROGRAM DOMINAN
      IMPLICIT INTEGER(A-Z)
      DOUBLE PRECISION TIMEA,TIMEB,TIMTOT,A(100,100),EPS,Z,ZBD,
     1               RDX1,RDX2,DTARG,DELTA,Z1,Z2,B(100,100),C(100,100),
     1               ZBEST,D(50,50,50),E(50,50)
      REAL S1
      INTEGER X(100),Q(100),S(100),SB(100),U(100)
C
C ######################################################################
C 9/27/03 This program uses branch-and-bound to find a reordering of the
C         rows and columns of an asymmetric proximity matrix so as to
C         maximize the sum above the main diagonal (DOMINANCE INDEX).
C ##################################################################
C
      OPEN(1,FILE='ASYM.DAT')
      OPEN(2,FILE='ASYM.OUT')
      EPS=1.0D-06
      READ(1,*) N                              ! Read number of objects
      DO I = 1,N
        READ(1,*) (A(I,J),J=1,N)               ! Read proximity matrix
      END DO
C
      CALL GETTIM (IHR, IMIN, ISEC, I100)
      CALL GETDAT (IYR, IMON, IDAY)
      TIMEA=DFLOAT(86400*IDAY+3600*IHR+60*IMIN+ISEC)+DFLOAT(I100)/100.
C
      ZBEST=0.
      DO III = 1,100                ! Use 100 replications of pairwise
        DO I = 1,N                  ! interchange to get an initial
          U(I)=I                    ! upper bound as well as a reorder
          S(I)=0                    ! of the raw data.
        END DO                      ! S(I) = object in position I
        DO I = N,1,-1               ! U vector contains unselected
          CALL RANDOM(S1)           ! objects.
          I1=FLOAT(I)*S1+1.         ! ZBEST is the best-found dominance
          IF(I1.GT.I) I1=I          ! index across the 100 reps.
          S(I)=U(I1)
          DO L = I1,N-1
            U(L)=U(L+1)
          END DO
        END DO
C
        Z=0.                        ! Calculate the sum above the main
        DO I = 1,N-1                ! diagonal, Z, for the initial
          DO J = I+1,N              ! permutation.
            Z=Z+A(S(I),S(J))
          END DO
        END DO
 5000   DTARG=EPS                   ! Begin the pairwise interchange
        DO I = 1,N-1                ! algorithm here
          DO J = I+1,N             
            R1=S(I)
            R2=S(J)
            DELTA=A(R2,R1)-A(R1,R2)
            DO K = I+1,J-1
              R3=S(K)
              DELTA=DELTA-A(R1,R3)+A(R2,R3)+A(R3,R1)-A(R3,R2)
            END DO
            IF(DELTA.GT.DTARG) THEN
              DTARG=DELTA
              ISEL=I
              JSEL=J
             END IF
          END DO
        END DO
        IF(DTARG.GT.EPS+EPS) THEN   ! Loop as long as improvement
          Z=Z+DTARG                 ! is found.
          JDUM=S(JSEL)
          S(JSEL)=S(ISEL)
          S(ISEL)=JDUM
          GO TO 5000
        END IF
        IF(Z.GT.ZBEST) THEN
          ZBEST=Z
          DO I = 1,N
            SB(I)=S(I)              ! SB will contain the actual reorder
          END DO                    ! of the raw data and will be used
        END IF                      ! again at the end to remap the
      end do                        ! object labels.
      Z=ZBEST-1
      DO I = 1,N
        S(I)=SB(I)
      END DO
      DO I = 1,N
        DO J = 1,N
          C(I,J)=A(S(I),S(J))
        END DO
      END DO
      DO I = 1,N
        DO J = 1,N
          A(I,J)=C(I,J)
        END DO
      END DO
      DO I = 1,N-1             ! B(I,J) is used for bound computation
        DO J = I+1,N              
          B(I,J)=A(I,J)
          IF(A(J,I).GT.B(I,J)) B(I,J)=A(J,I)
        END DO
      END DO
      DO 41 I = 1,N
        DO 42 J = 1,N
          IF(I.EQ.J) GO TO 42
          DO 43 K = 1,N
            IF(I.EQ.K.OR.J.EQ.K) GO TO 43
            D(I,J,K) = A(K,J)+A(J,I)-A(I,J)-A(J,K)
 43       CONTINUE
 42     CONTINUE
 41   CONTINUE
      DO 44 I = 1,N
        S(I)=0
        DO 45 J = 1,N
          IF(I.EQ.J) GO TO 45
          E(I,J) = A(I,J)-A(J,I)
 45     CONTINUE
 44   CONTINUE
C
C ######## BEGIN THE BRANCH-AND-BOUND ALGORITHM HERE ########
C
      M=1                      ! Pointer for sequence position
      Q(M)=1                   ! Object 1 in position M
      S(1)=1                   ! Object 1 is assigned
      Z1=0.
      DO K = 2,N
        Q(K)=0
        Z1=Z1+A(1,K)
      END DO
C
  1   M = M + 1                ! Advance pointer
C
  2   Q(M)=Q(M)+1              ! Increment object in position M
C
      IF(S(Q(M)).EQ.1) GO TO 2               ! REDUNDANCY
      IF(M.EQ.1.AND.Q(M).GT.N) GO TO 9       ! TERMINATE
      IF(M.GT.1.AND.Q(M).GT.N) GO TO 7       ! GO TO RETRACTION
      S(Q(M))=1
 22   IF(M.EQ.1) THEN               ! If object 1, then assign
        Z1=0.
        DO 24 J = 1,N
          IF(J.EQ.Q(M)) GO TO 24
          Z1=Z1+A(Q(M),J)
 24     CONTINUE
        GO TO 1
      END IF
      IF(M.EQ.N-1) THEN             ! If the sequence is complete,
        CALL EVAL(ZBD,Q,N,A)        ! call EVAL to assess the
        IF(ZBD.GT.Z) THEN           ! objective value for the
          Z=ZBD                     ! completed sequence
          write(*,*) z
          DO I = 1,N
            X(I)=Q(I)
          END DO
        END IF
        Q(N)=0
        S(Q(M))=0
        GO TO 2
      ELSE                         ! Otherwise Perform Tests
        R3=Q(M)
        R2=Q(M-1)
        IF(A(R3,R2).GT.A(R2,R3)) THEN   ! Adjacency Test
          S(Q(M))=0
          GO TO 2                       ! Prune if test fails
        END IF
        IF(A(R3,R2).EQ.A(R2,R3).AND.R3.LT.R2) THEN   ! Adjacency Test
          S(Q(M))=0
          GO TO 2                       ! Prune if test fails
        END IF
C        GO TO 154
        DO 152 MM = M-2,1,-1            ! INSERTION Test for
          RDX1=0.0D0                    ! with objects in positions
          DO I = MM,M-1
            R1=Q(I)
            RDX1=RDX1+E(R1,R3)
          END DO
          IF(RDX1.LT.-EPS) THEN         ! Prune if test fails
            S(Q(M))=0
            GO TO 2
          END IF
 152    CONTINUE
        GO TO 155
 154    DO 151 MM = M-2,1,-1            ! Interchange Test for
          R2=Q(MM)                      ! the object in poisition M
          RDX1=0.0D0                    ! with objects in positions
          DO I = MM+1,M-1
            R1=Q(I)
            RDX1=RDX1+D(R2,R1,R3)
          END DO
          RDX1=RDX1+A(R3,R2)-A(R2,R3)
          IF(RDX1.GT.EPS) THEN         ! Prune if test fails
            S(Q(M))=0
            GO TO 2
          END IF
 151    CONTINUE
 155    CALL BOUND2(ZBD,N,Q,M,S,A,Z1,Z2,B)   ! BOUND TEST
        IF(ZBD.LE.Z) THEN
          S(Q(M))=0
          GO TO 2                       ! Prune if test fails
        END IF
        Z1=Z1+Z2                        ! Branch further into tree
        GO TO 1                         ! if all tests pass.
      END IF
C
   7  S(Q(M))=0                         ! DEPTH RETRACTION
      Q(M)=0
      M=M-1                             ! Backup in the sequence
      R1=Q(M)
      DO 65 J = 1,N
        IF(S(J).EQ.1) GO TO 65
        Z1=Z1-A(R1,J)
 65   CONTINUE
      S(Q(M))=0
      GO TO 2
   9  CALL GETTIM (IHR, IMIN, ISEC, I100)
      CALL GETDAT (IYR, IMON, IDAY)
      TIMEB=DFLOAT(86400*IDAY+3600*IHR+60*IMIN+ISEC)+DFLOAT(I100)/100.
      TIMTOT=TIMEB-TIMEA
      WRITE(*,69) Z,TIMTOT
      WRITE(2,69) Z,TIMTOT
      WRITE(2,70) (SB(X(I)),I=1,N)
 69   FORMAT(' MAXIMUM DOMINANCE INDEX ',F14.4,' CPU TIME ',F8.2)
 70   FORMAT(30I3)
C
      END
C
      SUBROUTINE BOUND2(ZBD,N,Q,M,S,A,Z1,Z2,B)
      IMPLICIT INTEGER(A-Z)
      DOUBLE PRECISION A(100,100),ZBD,Z1,Z2,Z3,B(100,100)
      INTEGER Q(100),S(100)
      Z2=0.0D0
      R1=Q(M)
      DO 60 J = 1,N
        IF(S(J).EQ.1) GO TO 60
        Z2=Z2+A(R1,J)
 60   CONTINUE
C
      Z3=0.
      DO 61 I = 1,N-1
        IF(S(I).EQ.1) GO TO 61
        DO 62 J = I+1,N
          IF(S(J).EQ.1) GO TO 62
          Z3=Z3+B(I,J)
 62     CONTINUE
 61   CONTINUE
      ZBD=Z1+Z2+Z3
      RETURN
      END
C
      SUBROUTINE EVAL(ZBD,Q,N,A)
      IMPLICIT INTEGER(A-Z)
      DOUBLE PRECISION A(100,100),ZBD
      INTEGER Q(100)
      ZBD=0.0D0
      DO 85 I = 1,N
        DO J = 1,N-1
          IF(Q(J).EQ.I) GO TO 85
        END DO
        Q(N)=I
 85   CONTINUE
      DO I = 1,N-1
        R1=Q(I)
        DO J = I+1,N
          R2=Q(J)
          ZBD=ZBD+A(R1,R2)
        END DO
      END DO
      RETURN
      END

