Solution - The Gibbous, Non-Euclidean Program

by Joseph DeVincentis

Answer: Click here to reveal

It was 50 years ago this year when the Fortran 66 standard was published. Once you fix the backwardness of the text and the single-character typo in every line, you will get a working Fortran 66 program. Some relevant details:

  • If suitable packages are installed, you can compile it on a Linux system with g77 -ff66 source.f which will generate a program a.out which you can run.
  • Fortran 66 did not have the concept of opening named files. The program writes its output to unit 42, which might have been a printer, teletype, or even a card punch. On modern computers, this gets written to a file named fort.42 in your current directory.
  • All that 1HA, 1HB, ... nonsense is Hollerith string data. Fortran did not get the CHARACTER data type until Fortran 77, and they stored characters in any available variable (usually an array of integers, as many per integer as the machine allowed, though storing just one per integer like this program does was permitted). The number before the H indicated how many of the characters immediately after the H were read as string data. The A specifier in the FORMAT statement prints the Hollerith strings.
  • Except in Hollerith strings, and in the first 6 characters of a line, spaces did not matter in Fortran 66. You could insert them anywhere, even inside reserved words.
  • Only the first 72 characters in each line counted. When Fortran programs were stored on 80-column punch cards, the last 8 columns were often used to store line numbers which could be used to properly re-sort the program if it spilled (there were even machines for this specific purpose). Those line numbers often survived into versions of the code long after punch cards were no longer used.
  • The first character was reserved for a comment indicator, which is not used in this program. The second through fifth were used to store statement labels which code keywords such as DO and GOTO jumped to. The sixth, if it contained any character but 0 or space, indicated the line was a continuation of the previous line. One of these occurs in the DATA statement and you can use it to confirm the column positioning.

As suggested in the flavor text, exactly one character in each line of the program is incorrect. When you succeed in correcting the code, you will get this program:

      SUBROUTINE OUTPUT(X,R,L)                                          00000007
      IN TEGER X(L),R(L),L,B(28),J                                      00000008
      DATA B/1HA,1HB,1HC,1HD,1HE,1HF,1HG,1HH,1HI,1HJ,1HK,1HL,1HM,1HN,   00000009
     1     1HO,1HP,1HQ,1HR,1HS,1HT,1HU,1HV,1HW,1HX,1HY,1HZ,1H ,1H,/     00000010
 10   FORMAT(45A1)                                                      00000011
      DO 20 J=1,L                                                       00000012
         R(J)=B(X(J))                                                   00000013
 20   CONTINUE                                                          00000014
      WRITE(42,10)R                                                     00000015
      END                                                               00000016
      SUB ROUTINE DECODE(L,C,N,MSG)                                     00000017
      IN TEGER L,C(L),MDL,MSG(L),PRD,I,J                                00000018
      DO 40 J=1,L                                                       00000019
        MSG(J)=C(J)                                                     00000020
        PRD=C(J)                                                        00000021
        DO 30 I=1,4                                                     00000022
          PRD=MOD(PRD*PRD,N)                                            00000023
 30       CONTINUE                                                      00000024
        MSG(J)=MOD(MSG(J)*PRD,N)                                        00000025
 40   CON TINUE                                                         00000026
      END                                                               00000027
      SUBROUTINE UNCOMP(L,B,Z,MSG,MDL)                                  00000028
      INTEGER L,B,MDL,Z(B),MSG(L),J                                     00000029
      DO 50 J=1,L                                                       00000030
        Z(J*3)=MOD(MSG(J),MDL)                                          00000031
        Z(J*3-1)=MOD(MSG(J)/MDL,MDL)                                    00000032
        Z(J*3-2)=MSG(J)/MDL/MDL                                         00000033
 50   CONTINUE                                                          00000034
      END                                                               00000035
      INTEGER FUNCTION FFOS(N)                                          00000036
      INTEGER J                                                         00000037
      FFOS=1                                                            00000038
      DO 60 J=1,N                                                       00000039
         FFOS=FFOS*J                                                    00000040
 60   CONTINUE                                                          00000107
      RETURN                                                            00000108
      END                                                               00000109
      INTEGER FUNCTION FIM(N)                                           00000110
      INT EGER LAST,J,TMP                                               00000111
      LAST=0                                                            00000112
      FIM=1                                                             00000113
      DO 70 J=2,N                                                       00000114
         TMP=LAST                                                       00000115
         LAST=FIM                                                       00000116
         FIM=TMP+LAST                                                   00000117
 70   CONTINUE                                                          00000118
      RETURN                                                            00000119
      END                                                               00000120
      INTEGER FUNCTION PUL(N)                                           00000121
      INTEGER LAST,J,TMP                                                00000122
      LAST=2                                                            00000123
      PUL=1                                                             00000124
      DO 80 J=2,N                                                       00000125
         TMP=LAST                                                       00000126
         LAST=PUL                                                       00000127
         PUL=TMP+LAST                                                   00000128
 80   CONTINUE                                                          00000129
      RETURN                                                            00000130
      END                                                               00000131
      INTEGER FUNCTION CANK(J,K)                                        00000132
      INTEGER I,J,K,FFOS                                                00000133
      CANK=1                                                            00000134
      DO 90 I=1,K                                                       00000135
         CANK=CANK*(J+1-I)                                              00000136
 90   CONTINUE                                                          00000137
      CANK=CANK/FFOS(K)                                                 00000138
      RETURN                                                            00000139
      END                                                               00000140
      INTEGER Z(45),Q(45),I,C(15),MSG(15),N,JET,FFOS,FIM,IE,CANK,PUL    00000207
      REAL ELG                                                          00000208
      IBENJ(ELG)=INT(EXP(ELG))                                          00000209
      C(1)=FIM(24)                                                      00000210
      C(4)=PUL(2)+FFOS(4)*FFOS(2)                                       00000211
      C(2)=FFOS(5)*FIM(11)                                              00000212
      C(7)=IBENJ(4.0)*FIM(15)                                           00000213
      C(11)=CANK(16,7)                                                  00000214
      C(6)=CANK(15,7)+CANK(14,6)                                        00000215
      C(3)=IBENJ(7)*FFOS(4)                                             00000216
      C(13)=C(11)+IBENJ(9)                                              00000217
      C(15)=PUL(21)+CANK(14,7)                                          00000218
      C(8)=C(1)-PUL(19)                                                 00000219
      C(12)=PUL(20)+CANK(17,4)+IBENJ(6.0)+PUL(9)-FFOS(1)                00000220
      C(13)=C(13)+PUL(10)+PUL(8)+IBENJ(4)                               00000221
      C(6)=C(6)+PUL(5)+IBENJ(3)                                         00000222
      JET=FFOS(4)+FIM(5)                                                00000223
      C(15)=C(15)+PUL(10)+IBENJ(4)+CANK(4,2)                            00000224
      C(8)=C(8)-PUL(16)/FIM(3)-PUL(9)-IBENJ(0.0)                        00000225
      N=FFOS(8)+FIM(17)+3*FFOS(CANK(4,3))                               00000226
      C(11)=C(11)-FIM(16)+IBENJ(4)+FFOS(3)                              00000227
      C(5)=FIM(23)+FIM(19)-IBENJ(6.0)-FIM(7)+FIM(3)                     00000228
      C(14)=IBENJ(10.0)-IBENJ(4.0)-IBENJ(0.0)                           00000229
      C(9)=FIM(10)+FIM(24)/FIM(3)-FFOS(4)/FIM(3)                        00000230
      C(4)=C(4)*(IBENJ(5)-CANK(6,3))+MOD(C(2),2)                        00000231
      C(7)=C(7)+CANK(13,5)+PUL(5)+1+CANK(8,2)                           00000232
      C(3)=CANK(11,5)+FFOS(2)+C(3)                                      00000233
      C(10)=FFOS(8)+IBENJ(PUL(4))+IBENJ(5)+FIM(4)                       00000234
      C(1)=C(1)-IBENJ(9.0)+3*FIM(12)+PUL(5)-FIM(3)                      00000235
      C(2)=C(2)+IBENJ(6.0)+PUL(2)**FFOS(2)                              00000236
      CALL DECODE(15,C,N,MSG)                                           00000237
      CALL UNCOMP(15,45,Z,MSG,JET)                                      00000238
      CALL OUTPUT(Z,Q,45)                                               00000239
      END                                                               00000240

It prints one line of output:

OVERPUNCH TYPOS AND CORRECTIONS, THEN OVERLAY

Hopefully you were keeping track of where all those typos were in the code. It's finally time to use those three punch cards (the spooky, backward punch cards which have the upper-right corner cut instead of the upper-left corner). The line numbers (which start at 7, 107, and 207 on each page of the program) should suggest that you start a new card for each printed page, and that you start punching in column 7. The punching format is the standard one for these cards.

The correct letters and typos, per line, are:

  • page/card 1: SC, EI, AE, 19, OE, JZ, JD, CM, RH, DZ, RP, NF, OP, SU, DO, OD, RO, UH, PW, UG, DZ, UW, GX, DG, DB, ZY, DZ, UA, EA, OF, NM, SG, DG, OL
  • page/card 2: OA, TB, EA, GJ, EA, AO, MX, OT, AI, MB, PI, OA, TX, DB, FJ, PO, SZ, LQ, DT, TS, LZ, LF, CG, RP, ER, TH, TK, AO, OJ, CY, OD, FP, RP, DO
  • page/card 3: GE, RH, JT, FA, ST, SA, EU, CY, KZ, OU, BX, UW, PY, KE, UV, UW, FI, PY, UA, CR, IO, FR, JX, MG, KT, CS, SO, JT, UA, PN, AU, NX, PO, EW

Although there were variations with some symbols, the notation for letters and numbers needed here was consistent, and can be found in the Wikipedia article on punch cards.

The program output tells you that you need to overpunch the cards, punching the same card twice, once with the typos and once with the corrections. This will give you cards that may have up to four punches in some columns.

If done correctly, when you overlay the three cards, there will be one hole in many columns where you can see right through all three cards. These holes occur in rows (- for no hole) YY11-143947672-6980-9677-0011-4776.

Further overlay this on the shaded section of the program on page 3, backward (so the cut corner is at the left as it should be, the cards have the printed side down, and characters from the program starting at line 223 show through the holes. Reading column by column, this reads: JEFF SIMONCINI JEFF PAUL PUNK BAND.

This clues the answer, the name of their band, FINAL WARNING.

Program Details

Subroutine OUTPUT does just what its name implies. It takes the L integers in array X and interprets each as an index into the array of characters, B, which contains the alphabet plus a space and a comma, writes the corresponding Hollerith strings into the work array R, and writes it to unit 42.

42 is, of course, the answer to life, the universe, and everything, but was not so until 1979 when Douglas Adams wrote The Hitchhiker's Guide to the Galaxy. Anachronistic! Spooky!

Subroutine DECODE performs a crude RSA decryption of the data it receives, treating each element of the array C as a separate 16-bit message and using a 16-bit key. (Actually 15.5 bit, as the program does not take care to handle the numbers overflowing to negative if they exceed 231-1.) It uses a fixed exponent of 17 which is calculated through repeated squaring and multiplication. And yes, RSA in 1966. More anachronism.

Subroutine UNCOMP unpacks the decrypted message data, in which three numbers for the output have been packed in base 29 into a each element of the INTEGER array.

Functions FFOS, FIM, PUL, and CANK are factorial, Fibonacci, Lucas, and binomial coefficients, respectively, and statement function IBENJ is just the integer truncation of the exponential function.

The rest of the program builds up the values and key for the encrypted message in a convoluted way. All the typos in these lines are in the names of the functions.