Barrowes Consulting
Fortran consulting: code modernization, refactoring.
Matlab consulting: code development, GUIs, physics/electrical engineering
Specializing in fortran => Matlab conversion/translation/porting (see below)
The fortran goto statement allows jumps within a fortran program. Most goto's jump to the same or shallower nested if/do/while level. Jumps into a more deeply nested if/do/while were rare.
To have any hope of converting or translating fortran code to a more modern language like matlab/octave, python, etc., one has to remove every goto in the fortran code. I do this using while/break/continue combinations with a tool called remgoto.m I wrote. Remgoto analyzes the fortran code for goto's, then refactors the goto's by replacing them with do/while loops and branch statements. The resulting fortran code is logically and functionally equivalent to the original fortran code, but without the goto's. After this step, the fortran code can now be more readily converted into matlab/octave source code.
As an example, consider the following subroutine cbal.f from slatec:
subroutine CBAL (NM, N, AR, AI, LOW, IGH, SCALE) INTEGER I,J,K,L,M,N,JJ,NM,IGH,LOW,IEXC REAL AR(NM,*),AI(NM,*),SCALE(*) REAL C,F,G,R,S,B2,RADIX LOGICAL NOCONV RADIX = 16 B2 = RADIX * RADIX K = 1 L = N go to 100 20 SCALE(M) = J if (J == M) go to 50 DO 30 I = 1, L F = AR(I,J) AR(I,J) = AR(I,M) AR(I,M) = F F = AI(I,J) AI(I,J) = AI(I,M) AI(I,M) = F 30 CONTINUE DO 40 I = K, N F = AR(J,I) AR(J,I) = AR(M,I) AR(M,I) = F F = AI(J,I) AI(J,I) = AI(M,I) AI(M,I) = F 40 CONTINUE 50 go to (80,130), IEXC 80 if (L == 1) go to 280 L = L - 1 100 DO 120 JJ = 1, L J = L + 1 - JJ DO 110 I = 1, L if (I == J) go to 110 if (AR(J,I) /= 0.0E0 .OR. AI(J,I) /= 0.0E0) go to 120 110 CONTINUE M = L IEXC = 1 go to 20 120 CONTINUE go to 140 130 K = K + 1 140 DO 170 J = K, L DO 150 I = K, L if (I == J) go to 150 if (AR(I,J) /= 0.0E0 .OR. AI(I,J) /= 0.0E0) go to 170 150 CONTINUE M = K IEXC = 2 go to 20 170 CONTINUE DO 180 I = K, L 180 SCALE(I) = 1.0E0 190 NOCONV = .FALSE. DO 270 I = K, L C = 0.0E0 R = 0.0E0 DO 200 J = K, L if (J == I) go to 200 C = C + ABS(AR(J,I)) + ABS(AI(J,I)) R = R + ABS(AR(I,J)) + ABS(AI(I,J)) 200 CONTINUE if (C == 0.0E0 .OR. R == 0.0E0) go to 270 G = R / RADIX F = 1.0E0 S = C + R 210 if (C >= G) go to 220 F = F * RADIX C = C * B2 go to 210 220 G = R * RADIX 230 if (C < G) go to 240 F = F / RADIX C = C / B2 go to 230 240 if ((C + R) / F >= 0.95E0 * S) go to 270 G = 1.0E0 / F SCALE(I) = SCALE(I) * F NOCONV = .TRUE. DO 250 J = K, N AR(I,J) = AR(I,J) * G AI(I,J) = AI(I,J) * G 250 CONTINUE DO 260 J = 1, L AR(J,I) = AR(J,I) * F AI(J,I) = AI(J,I) * F 260 CONTINUE 270 CONTINUE if (NOCONV) go to 190 280 LOW = K IGH = L return end
This has 19 goto statements along with being older style f77. Even with a program like Polyhedron's spag, we still get the following:
SUBROUTINE CBAL(NM, N, AR, AI, LOW, IGH, SCALE) IMPLICIT NONE INTEGER , INTENT(IN) :: NM INTEGER , INTENT(IN) :: N INTEGER , INTENT(OUT) :: LOW INTEGER , INTENT(OUT) :: IGH REAL , INTENT(INOUT) :: AR(NM,*) REAL , INTENT(INOUT) :: AI(NM,*) REAL , INTENT(INOUT) :: SCALE(*) INTEGER :: I, J, K, L, M, JJ, IEXC REAL :: C, F, G, R, S, B2, RADIX LOGICAL :: NOCONV RADIX = 16 B2 = RADIX*RADIX K = 1 L = N GO TO 100 20 SCALE(M) = J IF (J /= M) THEN DO I = 1, L F = AR(I,J) AR(I,J) = AR(I,M) AR(I,M) = F F = AI(I,J) AI(I,J) = AI(I,M) AI(I,M) = F END DO DO I = K, N F = AR(J,I) AR(J,I) = AR(M,I) AR(M,I) = F F = AI(J,I) AI(J,I) = AI(M,I) AI(M,I) = F END DO ENDIF IF (IEXC == 1) GO TO 80 IF (IEXC == 2) GO TO 130 80 IF (L == 1) GO TO 280 L = L - 1 100 L120: DO JJ = 1, L J = L + 1 - JJ DO I = 1, L IF (I == J) CYCLE IF (AR(J,I)==0.0E0 .AND. AI(J,I)==0.0E0) CYCLE CYCLE L120 END DO M = L IEXC = 1 GO TO 20 END DO L120 GO TO 140 130 K = K + 1 140 L170: DO J = K, L DO I = K, L IF (I == J) CYCLE IF (AR(I,J)==0.0E0 .AND. AI(I,J)==0.0E0) CYCLE CYCLE L170 END DO M = K IEXC = 2 GO TO 20 END DO L170 SCALE(K:L) = 1.0E0 190 NOCONV = .FALSE. DO I = K, L C = 0.0E0 R = 0.0E0 DO J = K, L IF (J == I) CYCLE C = C + ABS(AR(J,I)) + ABS(AI(J,I)) R = R + ABS(AR(I,J)) + ABS(AI(I,J)) END DO IF (C==0.0E0 .OR. R==0.0E0) CYCLE G = R/RADIX F = 1.0E0 S = C + R 210 IF (C >= G) GO TO 220 F = F*RADIX C = C*B2 GO TO 210 220 G = R*RADIX 230 IF (C < G) GO TO 240 F = F/RADIX C = C/B2 GO TO 230 240 IF ((C + R)/F >= 0.95E0*S) CYCLE G = 1.0E0/F SCALE(I) = SCALE(I)*F NOCONV = .TRUE. AR(I,K:N) = AR(I,K:N)*G AI(I,K:N) = AI(I,K:N)*G AR(:L,I) = AR(:L,I)*F AI(:L,I) = AI(:L,I)*F END DO IF (NOCONV) GO TO 190 280 LOW = K IGH = L RETURN END SUBROUTINE CBAL
This is more modern and looks better, but the routine still has 12 goto's. Using remgoto, we get the following fortran90 code with no goto's, but instead has do/exit/cycle/if statements:
SUBROUTINE CBAL(NM, N, AR, AI, LOW, IGH, SCALE) IMPLICIT NONE logical :: remg(15)=.true. INTEGER , INTENT(IN) :: NM INTEGER , INTENT(IN) :: N INTEGER , INTENT(OUT) :: LOW INTEGER , INTENT(OUT) :: IGH REAL , INTENT(INOUT) :: AR(NM,*) REAL , INTENT(INOUT) :: AI(NM,*) REAL , INTENT(INOUT) :: SCALE(*) INTEGER :: I, J, K, L, M, JJ, IEXC REAL :: C, F, G, R, S, B2, RADIX LOGICAL :: NOCONV do if (remg(11)) then if (remg(6)) then if (remg(5)) then if (remg(4)) then if (remg(3)) then if (remg(2)) then if (remg(1)) then RADIX = 16 B2 = RADIX*RADIX K = 1 L = N remg(3)=.false.;cycle endif remg(1)=.true. 20 SCALE(M) = J IF (J /= M) THEN DO I = 1, L F = AR(I,J) AR(I,J) = AR(I,M) AR(I,M) = F F = AI(I,J) AI(I,J) = AI(I,M) AI(I,M) = F enddo DO I = K, N F = AR(J,I) AR(J,I) = AR(M,I) AR(M,I) = F F = AI(J,I) AI(J,I) = AI(M,I) AI(M,I) = F enddo ENDIF IF (IEXC == 1) then remg(2)=.false.;cycle endif IF (IEXC == 2) then remg(4)=.false.;cycle endif endif remg(2)=.true. 80 IF (L == 1) then remg(11)=.false.;cycle endif L = L - 1 endif remg(3)=.true. 100 DO JJ = 1, L!L120 J = L + 1 - JJ DO I = 1, L IF (I == J) then cycle endif IF (AR(J,I)==0.0E0 .AND. AI(J,I)==0.0E0) then cycle endif remg(12)=.false.;exit enddo if (.not.(remg(12))) then remg([12])=.true. ; cycle endif M = L IEXC = 1 remg(1)=.false.;exit enddo !L120 if (.not.(remg(1))) cycle remg(5)=.false.;cycle endif remg(4)=.true. 130 K = K + 1 endif remg(5)=.true. 140 DO J = K, L!L170 DO I = K, L IF (I == J) then cycle endif IF (AR(I,J)==0.0E0 .AND. AI(I,J)==0.0E0) then cycle endif remg(13)=.false.;exit enddo if (.not.(remg(13))) then remg([13])=.true. ; cycle endif M = K IEXC = 2 remg(1)=.false.;exit enddo !L170 if (.not.(remg(1))) cycle SCALE(K:L) = 1.0E0 endif remg(6)=.true. 190 NOCONV = .FALSE. DO I = K, L do if (remg(10)) then if (remg(9)) then if (remg(8)) then if (remg(7)) then C = 0.0E0 R = 0.0E0 DO J = K, L IF (J == I) then cycle endif C = C + ABS(AR(J,I)) + ABS(AI(J,I)) R = R + ABS(AR(I,J)) + ABS(AI(I,J)) enddo IF (C==0.0E0 .OR. R==0.0E0) then remg(14)=.false.;exit endif G = R/RADIX F = 1.0E0 S = C + R endif remg(7)=.true. 210 IF (C >= G) then remg(8)=.false.;cycle endif F = F*RADIX C = C*B2 remg(7)=.false.;cycle endif remg(8)=.true. 220 G = R*RADIX endif remg(9)=.true. 230 IF (C < G) then remg(10)=.false.;cycle endif F = F/RADIX C = C/B2 remg(9)=.false.;cycle endif remg(10)=.true. 240 IF ((C + R)/F >= 0.95E0*S) then remg(15)=.false.;exit endif G = 1.0E0/F SCALE(I) = SCALE(I)*F NOCONV = .TRUE. AR(I,K:N) = AR(I,K:N)*G AI(I,K:N) = AI(I,K:N)*G AR(:L,I) = AR(:L,I)*F AI(:L,I) = AI(:L,I)*F exit enddo if (.not.(remg(14).and.remg(15))) then remg([14,15])=.true. ; cycle endif enddo IF (NOCONV) then remg(6)=.false.;cycle endif endif remg(11)=.true. 280 LOW = K IGH = L exit enddo RETURN END SUBROUTINE CBAL
This can now be converted into matlab/octave code with f2matlab:
function [nm, n, ar, ai, low, igh, scale_ml]=cbal(nm, n, ar, ai, low, igh, scale_ml); persistent b2 c f g i iexc j jj k l m noconv r radix_ml remg s ; if isempty(remg), remg([1:15])=true; end; ar_shape=size(ar);ar=reshape([ar(:).',zeros(1,ceil(numel(ar)./prod([abs(nm)])).*prod([abs(nm)])-numel(ar))],abs(nm),[]); ai_shape=size(ai);ai=reshape([ai(:).',zeros(1,ceil(numel(ai)./prod([abs(nm)])).*prod([abs(nm)])-numel(ai))],abs(nm),[]); scale_shape=size(scale_ml);scale_ml=reshape(scale_ml,1,[]); if isempty(i), i=0; end; if isempty(j), j=0; end; if isempty(k), k=0; end; if isempty(l), l=0; end; if isempty(m), m=0; end; if isempty(jj), jj=0; end; if isempty(iexc), iexc=0; end; if isempty(c), c=0; end; if isempty(f), f=0; end; if isempty(g), g=0; end; if isempty(r), r=0; end; if isempty(s), s=0; end; if isempty(b2), b2=0; end; if isempty(radix_ml), radix_ml=0; end; if isempty(noconv), noconv=false; end; while (1); if(remg(11)) if(remg(6)) if(remg(5)) if(remg(4)) if(remg(3)) if(remg(2)) if(remg(1)) radix_ml = 16; b2 = radix_ml.*radix_ml; k = 1; l = fix(n); remg(3)=false; continue; end; remg(1)=true; scale_ml(m) = j; if(j ~= m) for i = 1: l; f = ar(i,j); ar(i,j) = ar(i,m); ar(i,m) = f; f = ai(i,j); ai(i,j) = ai(i,m); ai(i,m) = f; end; i = fix(l+1); for i = k: n; f = ar(j,i); ar(j,i) = ar(m,i); ar(m,i) = f; f = ai(j,i); ai(j,i) = ai(m,i); ai(m,i) = f; end; i = fix(n+1); end; if(iexc == 1) remg(2)=false; continue; end; if(iexc == 2) remg(4)=false; continue; end; end; remg(2)=true; if(l == 1) remg(11)=false; continue; end; l = fix(l - 1); end; remg(3)=true; %L120 for jj = 1: l; j = fix(l + 1 - jj); for i = 1: l; if(i == j) continue; end; if(ar(j,i)==0.0e0 && ai(j,i)==0.0e0) continue; end; remg(12)=false; break; end; if(~(remg(12))) remg([12])=true; continue; end; m = fix(l); iexc = 1; remg(1)=false; break; %L120 end; if(~(remg(1))) continue; end; remg(5)=false; continue; end; remg(4)=true; k = fix(k + 1); end; remg(5)=true; %L170 for j = k: l; for i = k: l; if(i == j) continue; end; if(ar(i,j)==0.0e0 && ai(i,j)==0.0e0) continue; end; remg(13)=false; break; end; if(~(remg(13))) remg([13])=true; continue; end; m = fix(k); iexc = 2; remg(1)=false; break; %L170 end; if(~(remg(1))) continue; end; scale_ml([k:l]) = 1.0e0; end; remg(6)=true; noconv = false; for i = k: l; while (1); if(remg(10)) if(remg(9)) if(remg(8)) if(remg(7)) c = 0.0e0; r = 0.0e0; for j = k: l; if(j == i) continue; end; c = c + abs(ar(j,i)) + abs(ai(j,i)); r = r + abs(ar(i,j)) + abs(ai(i,j)); end; j = fix(l+1); if(c==0.0e0 || r==0.0e0) remg(14)=false; break; end; g = r./radix_ml; f = 1.0e0; s = c + r; end; remg(7)=true; if(c >= g) remg(8)=false; continue; end; f = f.*radix_ml; c = c.*b2; remg(7)=false; continue; end; remg(8)=true; g = r.*radix_ml; end; remg(9)=true; if(c < g) remg(10)=false; continue; end; f = f./radix_ml; c = c./b2; remg(9)=false; continue; end; remg(10)=true; if((c + r)./f >= 0.95e0.*s) remg(15)=false; break; end; g = 1.0e0./f; scale_ml(i) = scale_ml(i).*f; noconv = true; ar(i,[k:n]) = ar(i,[k:n]).*g; ai(i,[k:n]) = ai(i,[k:n]).*g; ar([1:l],i) = ar([1:l],i).*f; ai([1:l],i) = ai([1:l],i).*f; break; end; if(~(remg(14)&&remg(15))) remg([14,15])=true; continue; end; end; if(noconv) remg(6)=false; continue; end; end; remg(11)=true; low = fix(k); igh = fix(l); break; end; return; end %subroutine cbal
This converted matlab/octave code can now be optimized and modified as an m-file as desired.
My background is in physics and electrical engineering as per my CV, dissertation, and other publications.
References upon request.
Contact: Ben Barrowes, barrowes@alum.mit.edu