Case Study 1: The Mystery Routine

"Before you can improve a thing, you must first be able to say, precisely, what it already does."

Executive Summary

You have inherited a codebase, and buried in it is a subroutine named FILTER with no comments, no documentation, and a note in the commit history that reads, in full, "works — don't touch." It uses a COMMON block, a statement function, implicit typing, and labeled DO loops, and you need to know what it computes before you dare to modernize it. This case study applies the five-step reading method from §17.6 to a genuinely unfamiliar routine — not the PLATE kernel you were guided through, but a fresh one — so that the method, not the memory of a particular program, is what you carry away. By the end you will have reconstructed the routine's meaning from its structure, confirmed it against a hand-traced example, and written a modern reconstruction that proves you understood it.

Skills applied: reading fixed-form source (§17.1); decoding a COMMON data model (§17.2); recognizing a statement function and labeled loops (§17.4); reasoning through implicit typing (§17.5); the five-step reading method (§17.6).

Background

Here is the routine, exactly as you found it, plus the small driver that exercises it. It is real, compilable fixed-form FORTRAN 77 — compile it with gfortran -std=legacy.

C     ==============================================================
C     (found in the codebase -- no documentation of any kind)
C     ==============================================================
      PROGRAM SMTEST
      COMMON /SIG/ X(6), N
      N = 6
      X(1) = 0.0
      X(2) = 0.0
      X(3) = 30.0
      X(4) = 0.0
      X(5) = 0.0
      X(6) = 0.0
      CALL FILTER
      WRITE (*,900) (X(I), I = 1, N)
900   FORMAT (1X, 6F6.1)
      STOP
      END

      SUBROUTINE FILTER
      COMMON /SIG/ X(6), N
      DIMENSION Y(6)
      AV3(A, B, C) = (A + B + C) / 3.0
      Y(1) = X(1)
      Y(N) = X(N)
      DO 10 I = 2, N-1
        Y(I) = AV3(X(I-1), X(I), X(I+1))
10    CONTINUE
      DO 20 I = 1, N
        X(I) = Y(I)
20    CONTINUE
      RETURN
      END

Do not run it yet. The discipline of reading is exactly the discipline of not reaching for the compiler to tell you what your own eyes can.

Phase 1 — Read the Shape, Not the Logic

Start with structure, ignoring every executable detail. There are two program units: a driver SMTEST and a subroutine FILTER. The driver fills six values, calls FILTER, and prints six values. So whatever FILTER does, it transforms an array of six numbers in place — the input is set before the call and the same array is printed after. Already, without reading one line of FILTER's body, you know its job description: it changes the signal X.

Phase 2 — Find the Shared State

The data model lives in the COMMON block. Both units declare COMMON /SIG/ X(6), N — identically, which is the first thing to verify and here it checks out. So FILTER communicates with its caller not through arguments but through shared memory: the array X and its length N. This is the classic FORTRAN 77 signature-free routine — it takes "no arguments" and yet operates on data, because the data is global.

COMMON /SIG/ entity Type (implicit) Role
X(6) REAL (starts with X) the six-element signal, shared in and out
N INTEGER (starts with N) the working length, here 6

Phase 3 — Identify the Loops

FILTER has two labeled DO loops. The first, DO 10 I = 2, N-1, runs over the interior indices only (2 through 5), skipping the two endpoints — a strong hint that the endpoints are special. The second, DO 20 I = 1, N, runs over all indices and copies Y back into X. So the routine computes something into a scratch array Y, then writes Y over X. The scratch-then-copy pattern is how you transform an array without corrupting the values you are still reading — the same reason PLATE kept a separate TNEW.

Phase 4 — Decode the Kernel

Two lines carry the meaning. The statement function

      AV3(A, B, C) = (A + B + C) / 3.0

names the average of three values, and inside the interior loop it is called as Y(I) = AV3(X(I-1), X(I), X(I+1)) — the average of each point with its two immediate neighbours. That is a three-point moving average, the simplest smoothing filter there is. The endpoint lines Y(1) = X(1) and Y(N) = X(N) pass the first and last values through unchanged (there is no neighbour beyond the edge to average with). The mystery is solved: FILTER smooths the signal X with a 3-point moving average, leaving the endpoints fixed.

Phase 5 — Confirm With a Hand Trace

Never trust a reading you have not tested against a number. The driver sets X = [0, 0, 30, 0, 0, 0] — a single spike of height 30. Trace the interior:

I AV3(X(I-1), X(I), X(I+1)) Y(I)
1 (endpoint) = X(1) 0.0
2 (0 + 0 + 30) / 3 10.0
3 (0 + 30 + 0) / 3 10.0
4 (30 + 0 + 0) / 3 10.0
5 (0 + 0 + 0) / 3 0.0
6 (endpoint) = X(6) 0.0

The spike of 30 is spread into three values of 10 — exactly what a smoothing filter does to an impulse. Now run it and confirm your reading:

$ gfortran -std=legacy mystery.f -o mystery && ./mystery
    0.0  10.0  10.0  10.0   0.0   0.0

The output matches the trace. You did not need the compiler to tell you what the routine does — you used it to confirm what you had already reconstructed. That is the correct relationship between reader and machine.

Phase 6 — Prove It With a Modern Reconstruction

The final test of understanding is being able to rebuild the thing cleanly. Here is FILTER reconstructed in modern Fortran — implicit none, a dp kind, an explicit array, no COMMON, no statement function, no labels — and it produces the identical output, which is your evidence that the reconstruction is faithful.

program smooth_modern
  use, intrinsic :: iso_fortran_env, only: dp => real64
  implicit none
  real(dp) :: x(6) = [0.0_dp, 0.0_dp, 30.0_dp, 0.0_dp, 0.0_dp, 0.0_dp]
  real(dp) :: y(6)
  integer  :: i, n

  n = size(x)
  y(1) = x(1);  y(n) = x(n)              ! endpoints pass through
  do i = 2, n - 1
    y(i) = (x(i-1) + x(i) + x(i+1)) / 3.0_dp   ! 3-point moving average
  end do
  x = y                                   ! whole-array copy replaces the loop

  print '(6f6.1)', x
end program smooth_modern
$ gfortran -std=f2018 -Wall smooth_modern.f90 -o sm && ./sm
   0.0  10.0  10.0  10.0   0.0   0.0

Identical values, and now the code says what it means: the array is explicit and length-bearing, the filter is one readable line, and the "copy back" is a whole-array assignment. The only cosmetic difference is the leading column — the legacy FORMAT carried a 1X, and the modern print does not — which is exactly the kind of trivial, non-numerical difference you learn to ignore when you check a modernization against its original.

Discussion Questions

  1. FILTER takes no arguments yet transforms data. What is the mechanism, and what are two concrete risks of a routine that communicates entirely through COMMON rather than through an argument list?
  2. In Phase 4, the interior-only loop bound (2, N-1) was a clue before you had decoded the kernel. What general reading habit does that illustrate — using loop bounds as evidence of intent?
  3. The modern reconstruction produces the same numbers but a slightly different printed layout. When you validate a modernization, how do you decide which output differences are "the same answer" and which are real regressions?

Your Turn: Extensions

  • Option A. Change the driver's input to a linear ramp, X = [10, 20, 30, 40, 50, 60], and predict the output before running. (Hint: what does a 3-point average of a straight line give? Connect it to why a linear field is a fixed point of neighbour-averaging — the discrete analogue of a linear function being exactly harmonic, the very fact that makes the boundary problems of this book solvable by hand.)
  • Option B. The routine hard-codes X(6) and N = 6. Modernize the interface so the filter works on an array of any length, using an assumed-shape argument (Chapter 6) instead of the COMMON block. What changes, and what disappears entirely?
  • Option C. Find a genuinely undocumented routine in a real open-source Fortran code (many are on GitHub), apply the five-step method, and write a one-paragraph "what it does" summary plus a hand-traced example. This is the exact skill you will sell to an employer sitting on legacy code.

Key Takeaways

  • The five-step method — shape, shared state, loops, kernel, hand trace — reconstructs an undocumented routine's meaning from its structure, before you change a line or even run it.
  • A signature-free FORTRAN 77 routine communicates through COMMON; finding the shared block is finding the data model, and it is always the second thing to do after reading the shape.
  • Loop bounds are evidence. An interior-only loop (2, N-1) advertises special endpoints; a scratch array copied back advertises an in-place transform that must not corrupt its own input.
  • You own a piece of legacy code when you can rebuild it cleanly and get the same answer. The modern reconstruction is not just tidier — it is your proof of understanding, and the reference for the modernization to come in Chapter 18.