[Next] [Up] [Previous] [Home] [Other]

FORTRAN IV

FORTRAN IV was a major change from FORTRAN II. Not only did it introduce several new features, but there were several incompatibilities between FORTRAN IV and FORTRAN II.

Function names no longer ended in F, and a function returned an integer value (by default) if it began with a letter from I through N, rather than the letter X, following the same rules as variables.

Variables could be explicitly given a type by a type declaration statement. Arrays could also be dimensioned in these statements, so that a separate DIMENSION statement would not be required, and variables could be initialized in those statements, so that a separate DATA statement, also a new statement for FORTRAN IV, would not be required.

Character strings in FORMAT statements could be enclosed in single quotes, rather than it being necessary to count the characters in them for the former syntax of a Hollerith format specification.

Explicitly declaring the type of variables allowed variables to be declared as DOUBLE PRECISION, LOGICAL, or COMPLEX.

The LOGICAL data type could only have two values, .TRUE. or .FALSE., and corresponded to the type of a relational expression in a logical IF statement. Thus, instead of writing

      IF (X-Y) 10,20,10
   10 CONTINUE

it was now possible to write

      IF (X .NE. Y) GO TO 20

The form of input-output statements was changed in FORTRAN IV as well. Instead of special purpose statements such as READ, PRINT, PUNCH, READ INPUT TAPE, and WRITE OUTPUT TAPE, there was simply READ (device, format) and WRITE (device, format). However, the BACKSPACE and REWIND statements continued to be available.

Also, the NAMELIST statement was provided, allowing list-directed input/output; on input, the variables to be changed could be indicated by name.

Functions and subroutines replaced the special statements that dealt with the sense switches and sense lights, as well as those that provided trapping of overflow and related conditions.

One important additional feature of FORTRAN IV was the ability to pass a function or subroutine to another function or subroutine as an argument. This meant, for example, that one could write a subroutine that did numerical integration, and pass to it the function to be integrated. This feature worked like this:

      EXTERNAL FUN
      X=ANUMIN(FUN,0.0,1.0)
      PRINT(6,11) X
      STOP
      END
      REAL FUNCTION FUN(X)
      FUN=X+TAN(X)
      RETURN
      END
      REAL FUNCTION ANUMINT(FN,ALOW,AHIGH)
      EXTERNAL FN
      AINC=(AHIGH-ALOW)*0.001
      SUM=0.0005*(FN(ALOW)+FN(AHIGH))
      DO 7 I=1,999
    7 SUM=SUM+FN(ALOW+FLOAT(I)*AINC)
      RETURN
      END

The versions of FORTRAN IV provided by IBM for the IBM System/360 included some extensions. REAL and DOUBLE PRECISION could be referred to as REAL*4 and REAL*8. COMPLEX*16, allowing complex numbers the real and imaginary parts of which were both double precision, was also one of these extensions.

As well, one could declare a variable to be of CHARACTER*n type, to reserve space for n characters. These variables would usually have to be filled with trailing blanks if they contained a short string, and no explicit character manipulation functions were included in the language, but the A format code, present even in the original FORTRAN language, permitted the input and output of these variables.

The IMPLICIT statement, allowing the first letter rule to be changed, was another extension to FORTRAN IV included on the System/360.

The first FORTRAN IV compiler, the one for the STRETCH, still included the FREQUENCY statement from the original FORTRAN compiler.


Here is an example of a FORTRAN IV program. It includes constants for converting from degrees to radians, and radians to degrees, expressed in internal binary form, which is machine dependent. Also note that it makes use of the assigned GO TO statement, because it repeatedly uses the same one of several alternatives in an inner loop: it is a program to draw a map of the world in one of several projections.

Aside from System/360 machine dependencies, this also calls routines to use an on-line plotter on I/O unit 9 that are peculiar to the particular MTS installation on which it was used: it also tested if it was being run from punched cards or from a terminal, to determine how it would ask for input, again using a routine peculiar to the operating system.

The version of FORTRAN used (or a local addition to its library) allowed free-format I/O, but the compiler did not allow putting an asterisk directly in the I/O statement, although a variable containing an asterisk was what indicated free-format I/O. So, as $ is the shift of * on a keypunch, and this version of FORTRAN allowed $ in variable names even as the first letter, a variable containing only an asterisk was given $ as its name.

Also, it is meant to work with either a CALCOMP plotter whose plotting area is 33 inches wide, or to be used from a Tektronix 4010 display, which is the reason for some of the less obvious portions of the code. It should be immediately obvious that CALL PLOT(X,Y,3) means to plot to point X, Y without drawing (with the pen up) and CALL PLOT(X,Y,2) means to draw to point X, Y from a perusal of the code.

A few comments have been added to the code as it stood originally; otherwise, it is not changed. In addition to an assigned GO TO being used to switch the program to the correct routine to calculate a point for the projection in use, another is used to allow the program to switch from plotting the map to the graticule, and another deals with various possible cases of whether or not a line should be drawn to the current point from the previous point.

This code does not illustrate particularly good programming style, and could thus be categorized as a youthful folly of mine, but it still does illustrate that even in the FORTRAN IV era, the assigned GO TO still can be put to good use shaving cycles from a program.

C THIS IS A PROGRAM TO DRAW MAPS OF THE WORLD
      REAL BUF(60),DTR/Z3F477D1B/,RTD/Z42394BB8/
      INTEGER FILE(20)
      REAL ASPA(12)/-1.,1.,1.5,2.,2.,1.,2.,1.8,0.,0.,0.,0./
      INTEGER IBF(58)/58*0/
      LOGICAL*1 LIB(232)
      INTEGER ITERC/0/
      LOGICAL*1 FIAR(44),ICAR(4)
      LOGICAL*1 $(1)/1H*/
      INTEGER*2 HX,HY
      LOGICAL*1 REPL
      EQUIVALENCE (LIB,IBF)
      EQUIVALENCE (ICAR,ITERC)
      ASSIGN 4406 TO IFIRS
      CALL PLOTS
      CALL GDINF('9       ',FIAR)
      ICAR(4)=FIAR(14)
C      ITERC IS 2 FOR TERMINAL, 3 FOR FILE
      GO TO (90801,90801,90802),ITERC
90801 ASSIGN 272 TO ITCL
      ASSIGN 4404 TO ITC2
      GO TO 90803
90802 ASSIGN 373 TO ITCL
      ASSIGN 4401 TO ITC2
90803 CONTINUE
      CALL CREPLY(&875)
      REPL=.TRUE.
      ASSIGN 575 TO IQIZ
  676 WRITE(6,991)
      WRITE(6,992)
      READ(5,$,END=909)AMR,AAR,AMS
      WRITE(6,981)
      READ(5,$)PLEN
      WRITE(6,971)
      WRITE(6,972)
      WRITE(6,973)
      READ(5,$)IPROJ
      WRITE(6,961)
      READ(5,$)ILAI,ILOI
      WRITE(6,951)
      WRITE(6,952)
      WRITE(6,953)
      READ(5,$)IGTO
      GO TO 287
  875 ASSIGN 878 TO IQIZ
      REPL=.FALSE.
      GO TO 878
  575 WRITE(6,1901)
  878 READ(5,$,END=909)AMR,AAR,AMS,PLEN,IPROJ,ILAI,ILOI,IGTO
  287 YCT=.5*PLEN
      ASPE=ASPA(IPROJ)
      IF (ASPE .GT. 0.0) GO TO 553
      IF(REPL)WRITE(6,941)
      READ(5,$)ASPE
  553 IF(PLEN-33.0)53,53,9999
   53 XCT=YCT*ASPE
      PWID=XCT+XCT
      SCALE=PWID*2.638889E-3
      GO TO ITC2,(4401,4404)
 4401 GO TO IFIRS,(4400,4406)
 4400 IF(IGTO)4404,4404,4402
 4402 YCM=YCUM+PLEN
      IF(YCM .GT. 33.0) GO TO 4403
      CALL PLOT(0.0,PLPR,-3)
      XMAX=AMAX1(XMAX,PWID)
      YCUM=YCN
      GO TO 4404
 4406 ASSIGN 4400 TO IFIRS
      GO TO 4405
 4403 CALL PLOT(XMAX,PLPR-YCUM,-3)
 4405 YCUM=PLEN
      XMAX=PWID
 4404 IGTO=IABS(IGTO)
      IF(IGTO-10)4408,4407,4407
 4407 IGTO=IGTO-10
      IF(REPL)WRITE(6,931)
      READ(5,31)FILE
      CALL SETLIO('2       ',FILE)
 4408 PLPR=PLEN
C INITIALIZATION FOR THE CHOSEN PROJECTION, INCLUDING DRAWING THE
C BORDER OF THE MAP
      GO TO (600,601,602,603,604,605,606,607,608,609,610,611),IPROJ
C MERCATOR
  600 QTR=DTR*.5
      AMX=PLEN*.48
      AMH=181.894*ASPE
      XXB=180.*SCALE
      XXC=XXB+XCT
      AXA=AMX+YCT
      CALL PLOT(XXC,AXA,3)
      XXD=XCT-XXB
      CALL PLOT(XXD,AXA,2)
      AXB=YCT-AMX
      CALL PLOT(XXD,AXB,2)
      CALL PLOT(XXC,AXB,2)
      CALL PLOT(XXC,AXA,2)
      ASSIGN 401 TO IPR
      GO TO 167
C AZIMUTHAL EQUIDISTANT
  601 RAD=SCALE*180.
      CALL PLOT(XCT,YCT+RAD,3)
      DO 27 ID=1,360
      ANGLE=DTR*FLOAT(ID)
      Y=YCT+RAD*COS(ANGLE)
      X=XCT+RAD*SIN(ANGLE)
   27 CALL PLOT(X,Y,2)
      ASSIGN 402 TO IPR
      GO TO 167
C SAVARD EGG
  602 RAD=SCALE*180.
      SRAD=SCALE*120.
      CALL PLOT(XCT,YCT+SRAD,3)
      DO 57 ID=1,360
      ANGLE=DTR*FLOAT(ID)
      X=XCT+RAD*SIN(ANGLE)
      Y=YCT+SRAD*COS(ANGLE)
   57 CALL PLOT(X,Y,2)
      ASSIGN 403 TO IPR
      GO TO 167
C SINUSOIDAL
  603 XMA=180.*SCALE
      CALL PLOT(YCT-90.*SCALE,16.5,3)
      DO 7631 ID=1,180
      ANGLE=FLOAT(ID-90)
 7631 CALL PLOT(XCT-XMA*COS(ANGLE*DTR),SCALE*ANGLE+YCT,2)
      DO 7632 ID=1,180
      ANGLE=FLOAT(90-ID)
 7632 CALL PLOT(XCT+XMA*COS(ANGLE*DTR),SCALE*ANGLE+YCT,2)
      ASSIGN 404 TO IPR
      GO TO 167
C ORTHOGRAPHIC
  604 XMH=90.*SCALE
      CALL PLOT(XCT,YCT,3)
      DO 7641 ID=2,360,2
      ANGLE=DTR*FLOAT(ID)
 7641 CALL PLOT(XCT+XMH-XMH*COS(ANGLE),XMH*SIN(ANGLE)+YCT,2)
      DO 7642 ID=2,360,2
      ANGLE=DTR*FLOAT(ID)
 7642 CALL PLOT(XCT+XMH*COS(ANGLE)-XMH,XMH*SIN(ANGLE)+YCT,2)
      ASSIGN 405 TO IPR
      GO TO 167
C LAMBERT AZIMUTHAL EQUAL-AREA
  605 RAD=SCALE*180.
      SS=.5*DTR
      ASSIGN 406 TO IPR
      GO TO 167
C HAMMER-AITOFF
  606 XSH=SCALE*90.
      XMH=XSH+XSH
      SS=.5*DTR
      XS=127.2792
      SF=2.0
      CALL PLOT(XCT,XSH+YCT,3)
      DO 7661 ID=1,360
      ANGLE=DTR*FLOAT(ID)
 7661 CALL PLOT(XMH*SIN(ANGLE)+XCT,XSH*COS(ANGLE)+YCT,2)
      ASSIGN 407 TO IPR
      GO TO 167
C AITOFF EQUAL-AREA
  607 SF=1.8
      XS=141.4214
C     MULTIPLIES BY ROOT 2, TAKING FIXED +/- 90 OF PROJ TO 1/SF FOR VERT
      SS=.5*DTR
      RAD=SCALE*180.
      SRAD=SCALE*100.
      DO 7682 IE=1,2
      CALL PLOT(XCT+RAD*.4358899,YCT+SRAD*.9,3)
      DO 7681 ID=1,100
      ANGLE=FLOAT(ID-50)*2.39539E-2
C     ARC SINE .9 EQUALS 64.158 DEGREES OR 1.19769515 RADIANS
C     ARC SINE .75 EQUALS 48 DEGREES, 35 MINUTES OR .84806208 RAD
 7681 CALL PLOT(XCT+RAD*COS(ANGLE),YCT-SRAD*SIN(ANGLE),2)
 7682 RAD=-RAD
      ALATH=1.197695
 7684 ILON=-180
      IC=3
      ASSIGN 65 TO IQIC
 7683 ALON=FLOAT(ILON)
      ALAT=ALATH
      GO TO 1478
 2678 ILON=ILON+1
      IF(ILON.LT.180)GO TO 7683
      ALATH=-ALATH
      IF (ALATH .LT. 0.) GO TO 7684
      ASSIGN 408 TO IPR
      GO TO 167
C CARDS TO ENSURE LABELS ARE DEFINED FOR FUTURE EXPANSION
  608 ASSIGN 409 TO IPR
  609 ASSIGN 410 TO IPR
  610 ASSIGN 411 TO IPR
  611 ASSIGN 412 TO IPR
  167 CONTINUE
      ASSIGN 82 TO ILAC
      IF(ABS(AAR) .LT. .009) GO TO 276
C ROTATIONS UNDER .01 DEG, NOT ALLOWED: IF NO ROTATION OF
C COORDINATES, INITIALIZATION AND DIFFICULT PART OF ROTATION SKIPPED.
      AAR=AAR*DTR
      CA=COS(AAR)
      SA=SIN(AAR)
      ASSIGN 86 TO ILAC
  276 IF  (IGTO-2) 376,99,376
  376 ASSIGN 205 TO IPPR
      REWIND 2
C THIS STATEMENT READS A RECORD THE FIRST FIVE CHARACTERS OF WHICH GIVE
C THE NUMBER OF POINTS IN A STRETCH OF COASTLINE
  476 READ(2,11,END=9099)ICO
      IC=3
      ASSIGN 85 TO IQIC
C THESE POINTS WILL BE GIVEN ON SUBSEQUENT RECORDS, THIRTY POINTS TO A
C RECORD
C THESE RECORDS HAVE THE FOLLOWING FORMAT:
C SIX SPACES
C THE LATITUDE OF THE FIRST POINT, AS A 16-BIT INTEGER IN TENTHS OF A
C     DEGREE, IN A2 FORMAT
C ONE SPACE
C THE LONGITUDE OF THE FIRST POINT, AS A 16-BIT INTEGER IN TENTHS OF A
C     DEGREE, IN A2 FORMAT
C ONE SPACE
C DELTA LAT AND DELTA LON, IN TENTHS OF A DEGREE FOR THE REMAINING
C     29 POINTS, IN EXCESS-128 FORM
    4 IK=MIN0(ICO,30)
      IP=IK+IK-2
      IIP=IP*4
      IF(IP)476,48,44
C IF ONLY ONE POINT REMAINS TO BE READ, OMIT THE IMPLIED DO
   48 READ(2,21)HX,HY
      IHX=HX
      IHY=HY
      BUF(1)=FLOAT(IHX)/10.
      BUF(2)=FLOAT(IHY)/10.
      GO TO 500
   44 READ(2,21)HX,HY,(LIB(I),I=4,IIP,4)
      IHX=HX
      IHY=HY
      BUF(1)=FLOAT(IHX)/10.
      BUF(2)=FLOAT(IHY)/10.
      DO 7 J=1,IP
    7 BUF(J+2)=BUF(J)+FLOAT(IBF(J)-128)/10.
  500 IO=IK+IK-1
      J=1
   17 CONTINUE
      ALAT=BUF(J)
      ALON=BUF(J+1)
C PROCEED TO PROGRAM TO PLOT POINTS
      GO TO 222
  205 CONTINUE
      IF(J.LE.10)GO TO 17
      ICO=ICO-30
      GO TO 4
 9899 IF(IGTO-1)373,373,99
   99 ASSIGN 215 TO IPPR
      IMLL=MIN0(-80,-90+ILAI)
      IXLL=0-IMLL
      ILON=ILOI-180
      ALONH=ALON
      IC=3
      ASSIGN 65 TO IQIC
      ILAT=IMLL
  420 ALAT=FLOAT(ILAT)
      ALON=ALONH
      GO TO 222
  215 CONTINUE
      ILAT=ILAT+1
      IF (ILAT.LE.IXLL) GO TO 420
      ILON=ILON+ILOI
      IF (ILON.LE.180) GO TO 4410
      ASSIGN 225 TO IPPR
      ILAM=90-ILAI
      ILAT=ILAI-90
  510 ALAT=FLOAT(ILAT)
      ASSIGN 65 TO IQIC
      IC=3
      ILON=-180
  520 ALON=FLOAT(ILON)
      ALAT=ALATH
      GO TO 222
  225 CONTINUE
      ILON=ILON+1
      IF(ILON.LE.180)GOTO 520
      ILAT=ILAT+ILAI
      IF(ILAT .LE. ILAM) GO TO 510
      GO TO ITCL,(272,373)
  272 CALL PLOT(0.0,0.0,-3)
  373 GO TO IQIZ(878,676,575)
  909 CALL PLOT(0.0,0.0,999)
      STOP
 9999 WRITE(6.9961)
      STOP
  222 CONTINUE
C CARDS TO ACTUALLY PLOT POINT FOLLOW.
C ****************************************** 1
C ROTATION OF AXES
      ALON=ALON-AMR
C ASSIGNED GO TO USED TO SPEED PROGRAM
      GO TO ILAC,(86,82)
   86 ALON=ALON*DTR
      ALAT=ALAT*DTR
      Z=SIN(ALAT)
      X=COS(ALAT)
      Y=X*SIN(ALON)
      X=X*COS(ALON)
      ZN=Z*CA-X*SA
      XN=X*CA+Z*SA
      ALAT=ARSIN(ZN)*RTD
      IF(ABS(XN)+ABS(Y)-.002)350,360,360
  350 ALON=0.
      GO TO 65
  360 ALON=ATAN2(Y,XN)*RTD
   82 ALON=ALON+AMS
C ANGLES HAVE NOW BEEN ROTATED, AND ARE IN DEGREES.
C THEIR SIGN IS CORRECT, AND USING ATAN2 TO GET ALON
C PREVENTS BLOWUP NEAR 0 OR 90 DEG.
      ALON=AMOD(ALON+900.,360.)-180.
C IF AMS IS WITHIN +/- 180. THIS WILL GIVE ANGLE PROPER RANGE.
      GO TO IQIC,(65,75,85,95)
C IF DECIDED IN ADVANCE NOT TO PLOT POINT, SKIP BOUNDARY CHECK,
C OR IF FIRST POINT OF RUN, TO BE ALWAYS PLOTTED TO WITH PEN UP.
   95 IF (ALON*AFIR.LE.0.) IC=3
   75 IF(ABS(ALON-AFIR).GT.100.) IC=3
   65 CONTINUE
C ACTUAL PROCEDURE TO PERFORM PROJECTION NOW BEGINS.
      GO TO IPR,(401,402,403,404,405,406,407,408,409,410,411,412)
C MERCATOR
  401 X=ALON
      Y=RTD*ALOG(TAN(QTR*(ALAT+90.)))
      IF (ABS(Y)-AMH)660,660,949
C AZIMUTHAL EQUIDISTANT
  402 R=90.-ALAT
      IF(R.GT.179.8) GO TO 949
      ALON=ALON*DTR
      Y=R*SIN(ALON)
      X=R*COS(ALON)
      GO TO 665
C SAVARD EGG
  403 ALATA=ABS(ALAT)
      ALONA=ABS(ALON)
      YZR=ALATA+ALATA*ALATA*ALATA/24300.
      X=ALONA*SQRT(8100.-ALATA*ALATA)*1.111111E-2
      DELT=1.333333*ALATA-YZR
      IF (ALATA .LE. .1) GO TO 1476
      RAD=.5*DELT+2.*(8100.-ALATA*ALATA)/DELT
      Y-YZR+RAD-SQRT(RAD*RAD-X*X)
 6601 X=SIGN(X,ALON)
      Y=SIGN(Y,ALAT)
      GO TO 660
C CLOSE TO THE EQUATOR, PARABOLAE APPROXIMATE ARCS TO AVOID NUMERICAL PROBLEMS
 1476 Y=YZR+DELT*ALONA*ALONA/32400.
      GO TO 6601
C SINUSOIDAL
  404 Y=ALAT
      X=ALON*COS(ALAT*DTR)
      GO TO 660
C ORTHOGRAPHIC
  405 ALAT=ALAT*DTR
      Y=90*SIN(ALAT)
      X=SIGN(90.,ALON)+COS(ALAT)*SIN((ABS(ALON)-90.)*DTR)*SIGN(90.*ALON)
      ASSIGN 90 TO IQIC
      GO TO 665
C LAMBERT AZIMUTHAL EQUAL AREA
  406 R=90.-ALAT
      IF (R.GT.179.8) GO TO 949
      R=180.*SIN(SS*R)
      ALON=ALON*DTR
      X=R*COS(ALON)
      Y=Y*SIN(ALON)
      GO TO 665
C HAMMER-AITOFF
  407 ALAT=ALAT*DTR
 1470 ALONHA=SS*ALON
      Z=SIN(ALAT)
      X=COS(ALAT)
      Y=X*SIN(ALONHA)
      X=X*COS(ALONHA)
      ALAT=ARSIN(X)*RTD
      IF(ABS(Z)+ABS(Y)-.002)881,882,882
  881 ALON=0.
      GO TO 883
  882 ALON=ATAN2(Z,Y)
  883 R=XS*SIN(SS*(90.-ALAT))
      Y=R*SIN(ALON)
      X=SF*R*COS(ALON)
      GO TO 660
  408 ALAT=ARSIN(.9*SIN(DTR*ALAT))
      GO TO 1476
  409 CONTINUE
  410 CONTINUE
  411 CONTINUE
  412 CONTINUE
C |||||||||||||||||||||||||||||||||||||||||| 2
C RETURN TO CONVERSION SEGMENT TO PLOT POINT.
  949 IC=3
      GO TO 999
  660 ASSIGN 75 TO IQIC
  665 YYO=Y*SCALE+YCT
      XXO=X*SCALE+XCT
      CALL PLOT(XXO,YYO,IC)
C ****************************************** 1
C RETURN TO POINT CO-ORDINATING EXTERNAL PROGRAM.
      IC=2
  999 CONTINUE
      GO TO IPPR,(205,215,225,2670)
 9961 FORMAT(' PLOT TOO LARGE: TALLER THAN 33 IN.')
  931 FORMAT(' ENTER NEW MAP FILE NAME, FOLLOWED BY AT LEAST ONE BLANK')
  941 FORMAT(' ENTER ASPECT RATIO (WIDTH/HEIGHT) OF PLOT DESIRED.')
  951 FORMAT(' ENTER GRATICULE OPTION: 1-MAP ONLY, 2-GRATICULE ONLY,')
  952 FORMAT(' 3-MAP AND GRATICULE: MAKE NEGATIVE IF PREVIOUS MAP')
  953 FORMAT(' IS TO BE OVERDRAWN,+10 TO RESPECIFY MAP FILE')
  961 FORMAT(' ENTER LAT. AND LONG. INCREMENTS FOR GRATICULE')
  971 FORMAT(' ENTER PROJECTION NUMBER: 1-MERCATOR, 2-AZIM. EQUIDIST.')
  972 FORMAT(' 3-SAVARD EGG,4-SINUSOIDAL,5-ORTHOGRAPHIC')
  973 FORMAT(' 6-LAMBERT AZIMUTHAL EQUAL-AREA,7-HAMMER-AITOFF,')
  981 FORMAT(' ENTER HEIGHT OF DESIRED MAP')
  991 FORMAT(' ENTER MERIDIAN OF ROTATION, AMOUNT OF ROTATION,')
  992 FORMAT(' MERIDIAN TO WHICH IT IS ASSIGNED AFTERWARDS,')
 1901 FORMAT(' MR/AR/MA:PLEN:PROJ:LATI/LONI:GRATOPS')
   11 FORMAT(I5)
   21 FORMAT(6X,A2,1X,A2,1X,58A1)
   31 FORMAT(20A4)
      END

The FORTRAN 66 standard codified FORTRAN IV in an official standard; since FORTRAN IV had been in existence since 1963, however, except in contexts where standards conformance was specified, FORTRAN 66 never really caught on as the name of this form of the language.

Copyright (c) 2007 John J. G. Savard


[Next] [Up] [Previous] [Home] [Other]