Setting-up Early Computer Programs: D. H. Lehmer's ENIAC Computation (Archive for Mathematical Logic 49, 123–146)
Setting-up early computer programs: D. H. Lehmer’s ENIAC
computation
Maarten Bullynck, Liesbeth de Mol
To cite this version:
Maarten Bullynck, Liesbeth de Mol. Setting-up early computer programs: D. H. Lehmer’s ENIAC computation. Archive for Mathematical Logic, 2010, 49, pp.123-146. ⟨hal-01396497⟩
HAL Id: hal-01396497
https://lilloa.hal.science/hal-01396497v1
Submitted on 14 Nov 2016
HAL is a multi-disciplinary open access archive
for the deposit and dissemination of scientific research documents, whether they are published or not.
The documents may come from teaching and research
institutions in France or abroad, or from public or private research centers.
L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant
des établissements d’enseignement et de recherche
français ou étrangers, des laboratoires publics ou
privés.
HAL Authorization
Setting-up early computer programs. D.H.
Lehmer’s ENIAC computation.∗
Maarten Bullynck†
Liesbeth De Mol‡
April 21, 2010
Abstract
A complete reconstruction of D.H. Lehmer’s ENIAC set-up for computing the exponents of p modulo 2 is given. This program served as an
early test program for the ENIAC (1946). The reconstruction illustrates
the difficulties of early programmers to find a way between a man operated and a machine operated computation. These difficulties concern
both the content level (the algorithm) and the formal level (the logic of
sequencing operations).
Keywords:ENIAC History of Computer Programs Number Theory Derrick H. Lehmer
MSC(2000):11-04 11N36 11A07 01A60 11Y16
1
Introduction
In 1943 the Army’s Ordnance Proving Ground at Aberdeen Maryland contracted John W. Mauchly and Prespert J. Eckert to build the ENIAC, the first
U.S. electronic digital and (basically) general-purpose computer. Its original
purpose was the computation of ballistic trajectories. However, due to its architecture, it could be used to solve many more problems. Already in 1945 the
Ordnance also assembled a “Computations Committee” that had to prepare
for utilizing the machine after its completion. The Committee consisted of the
number theorist D.H. Lehmer, the logician H.B. Curry, the astronomer L.B.
Cunningham and the statistician F.L. Alt [1]. Each developed their own test
program to be run on the ENIAC after it was first presented to the public at
Penn University February 15, 1946. Soon afterwards, through John von Neumann, the Atomic Energy Commission got interested in using the ENIAC for
computations connected to the development of the H-bomb.
∗ This research was supported by the Flemish Fund for Scientific Research (FWO- Flan-
ders) and the Humboldt Foundation. A version of this paper has been published in Archive
for Mathematical Logic, vol. 49, nr. 2, 123-146. The original publication is available at
www.springerlink.com
† [email protected], Université Paris 8, Vincennes à Saint-Denis, France
‡ [email protected], CLPS, Universiteit Gent, Blandijnberg 2, 9000 Gent, Belgium
1
These test programs, the ballistic trajectory computations and especially the
problems that the Atomic Energy Commission wanted to put on ENIAC soon
made clear the difficulties and limitations of “programming” the ENIAC in its
original hardware configuration [2, 3]. For each new problem, the ENIAC had
to be programmed directly and locally, setting the switches on each individual
unit, laying the cables to interconnect these units and control the timing and
sequencing of the units’ operations. The units, 30 in number, operated in parallel, which made the timing and sequencing a tricky job. Moreover, the amount
of dynamic (“RAM”) and static (“ROM”) memory was limited. The memory
problem could, however, be circumvented by using punch cards to store (intermediate) results and inputting them into the computation at a given point.
Again, the synchronization of this was a difficult task. Programming the ENIAC
in its original configuration thus came down to “the design and development of
a special-purpose computer out of ENIAC component parts” [2, p. 32]. Or as
Jean Bartik, one of the ENIAC’s female programmers, put it, the ENIAC “was
a son-of-a-bitch to program” [4, p. 94].
As a consequence, ways of simplifying the ENIAC’s set-up were contemplated. Initially, often recurring instructions were codified as two-digit numbers
that could be looked up in the function table unit that in its turn triggered the
right program pulses [5, sec. 7.4]. A logical coding system consisting of a 60word vocabulary of orders was put on the ENIAC in the latter half of 1947. Mid
1948 a 100-word converter code was developed and installed on the ENIAC (i.e.,
the ENIAC was rewired, switches set semi-permanently) [6, 7]. This code was
often expanded and revised during the following years and turned the ENIAC
into a “more effective serial stored-program computer” [2, p. 32–33].1 The setup of a new problem on ENIAC was thus considerably simplified, though at a
cost. The running time increased with a factor of six on the average.
The early (declassified) test programs are unique instances of “programming” a machine that had not the kind of logical design we know nowadays as
the von Neumann architecture. Furthermore any kind of programming language
was totally absent. These programs demonstrate the difficulties of adapting
computations made to human measure to a machine. Also various solutions
towards logically organizing the processes within that computation had to be
invented. The current reconstruction of D.H. Lehmer’s ENIAC program extends
and completes the partial reconstruction presented in [9] and discusses in detail
Lehmer’s early attempt at “programming”. Furthermore, the reconstruction of
the sieve is now improved by removing certain redundancies.
1 There is much controversy regarding the invention of the stored-program computer. We
will not enter into this discussion but refer to the lucid discussion in [8].
2
2
ENIAC: Architecture and Program Planning
2.1
Overview of ENIAC
The Electrical Numerical Integrator And Computer (ENIAC) was first described
in a proposal John Mauchly submitted to Penn University, and was ultimately
built with U.S. Army money by a team of engineers under the direction of Presper Eckert Jr. It used about 18,000 vacuum tubes and 1,500 relays. From 1945
to 1947 this was a highly parallel computer, though its parallelism was hardly
ever used [10, p. 376]. Lehmer’s program was one of the notable exceptions.
The ENIAC had a modular architecture. It comprised 20 accumulators,
a multiplier, a divider and square rooter, a constant transmitter, 3 function
tables, a master programmer, a cycling unit, an initiating unit and also a card
reader and a printer. The constant transmitter and the function tables were
the ENIAC’s main (static) memory storage units (“ROM”). The accumulators
were the main (dynamic) memory storage units that could be changed during a
computation (“RAM”), each storing a signed 10-digit number. However, each
accumulator had 12 program controls which “gives us 240 components we can
use in the computational dataflow.” [11, p. 20]. Furthermore each accumulator
could be used to store more than one number. E.g. it could be used to process
two 5-digit numbers with the same sign. These could be processed separately
by using special shifter and deleter adaptors (See Sec. 3.3.4).2
Another way to extend the ENIAC’s small amount of both memory types is
the following. The constant transmitter (CT) consisted of two panels: (CT2)
where numbers could be stored by manually setting the switches before computation, and (CT1) where the counters were set automatically according to the
input that was read on a punched card fed into the card reader. This could be
done during a computation, though that required some ingenuity to synchronize the (slow) reading of the punched card with the steps in the computation.
Therefore, the designers of the ENIAC added an interlock to this unit, to simplify exactly this kind of synchronization [5, sec. 8.2.4].3 With the card reader
and the (CT1) unit new numbers could be brought into the computation during
runtime.
Of crucial importance in the ENIAC was the central programming pulse
(CPP), emitted by the cycling unit once every 1/5000th of a second, marking
the beginning and end of a computation cycle. These pulses synchronized the
operations of the units. When a unit completed an operation it emitted one of
these as a program output pulse, stimulating the next operation. All the units
required an integral number of cycles. E.g. an accumulator required 1/5000th
of a second for an addition, therefore we speak also of an addition time instead
of a cycle.
The ENIAC had two kinds of electronic circuits: the numerical circuits for
storing and processing electric signals representing numbers and programming
2 The possibility of splitting one number into two smaller numbers is also possible in the
function tables and the constant transmitter.
3 For similar reasons, also the printer unit and the division units had such an interlock.
3
circuits for controlling the communication between the different parts of the
machine. Most of the units had both kind of circuits, except for the master programmer and the initiating unit which consisted only of programming circuits.
Numerical pulses have a range of 0P to 9P (a 10P also exists) with 1P being
the unit voltage. Program pulses always have a voltage of one CPP (=1’P).
Numerical and programming pulses are strictly separated. There was however
a technique to convert a digit pulse into a program pulse through the use of a
special digit-to-program pulse adaptor and one or two dummy programs (See
Sec. 2.4.1, 2.4.2).
Our further discussion of the ENIAC will focus on the salient “programming”
facilities, that is, mainly the accumulator and the master programmer. In this
discussion we have made use of various references. The most detailed technical
description of the ENIAC is [5], less detailed but instructive and rather readable
accounts are [10, 12, 13]. We would also like to refer to recent research on the
ENIAC: [14] analyzed the ENIAC’s architecture and used VLSI to put ENIAC
on a chip; [15, 11] present (incomplete) Java simulations of ENIAC.4
2.2
Accumulators
The accumulators were the main arithmetic units of the ENIAC and could be
used to add or subtract a number. Fig. 1 gives a graphical representation of
an accumulator. Each accumulator held a 10 place decimal number and a sign
(P for plus and M for minus), stored in ten decade ring counters and a PM
counter. It had 5 input channels (α to ǫ) to receive a number. It had two output channels (A and S) to transmit a number n (through A) or its complement
1010 − n (through S). In one addition time, the accumulator could either receive
a number n, adding (if n ≥ 0) or subtracting (if n < 0) it to/from its content,
or transmit the number it stored through one or both of its output channels.
The program part of the accumulator consisted of 12 program controls: 4 receivers and 8 transceivers. A transceiver had a program pulse input and output
terminal, a clear-correct switch (to clear or not clear its content after a cycle;
it could also be used to round off numerical results), an operation switch (to be
set to α-ǫ, A, S, AS or 0, determining whether the accumulator should receive
or transmit a number, or do nothing) a repeat switch (with which it could either
receive or transmit up to 9 times). When a transceiver received a program pulse
through a program cable at addition time r, the operations set on the program
switch associated with that transceiver were executed. When these had been
finished after n (1 ≤ n ≤ 9) addition times, a program pulse was transmitted
through the output of the transceiver at addition time r + n. A receiver differs
from a transceiver in that it has no output terminal and no repeater switch. An
accumulator furthermore contains a significant figures switch. This is used to
round off numerical results. If the significant figures switch is set to a number
4 More particularly, the master programmer, the function tables, the card reader, the
printer and the special multiplication and division combinations of accumulators are missing in the Java simulations. The source code of Hansen’s Java simulation can be found at:
http://home.arcor.de/-ph/eniac/download.html.
4
s, then, when clearing takes place using the clear-correct switch, decade 10 − s
clears to 5 and all other decades to 0.
α
β
γ
ǫ
δ
A
Input Channels
S
Output Channels
Receivers
o/c
o/c
o/c
o/c
In
In
In
In
SF
Transceivers
Transceivers
o/c
o/c
o/c
o/c
o/c
o/c
o/c
o/c
In
Out
In
Out
In
Out
In
Out
In
Out
In
Out
In
Out
In
Out
r
r
r
r
r
r
r
r
Figure 1: A Schematic Representation of an Accumulator. Here, “SF” is the
significant figures switch, “In” rsp. “In/Out” the operation switches of the 4
receivers rsp. the 8 transceivers, “r” the repeater switch and “o/c” the clearcorrect switch for the 12 program controls. On the bottom, an incoming arrow
indicates that a program pulse can be received, an outgoing arrow that a program pulse can be sent. At the top, α to ǫ are the 5 input terminals and A and
S the two output terminals.
2.3
Master Programmer: Loops and Sequencing
The master programmer provided a certain amount of centralized programming
memory. It consisted of 10 independently functioning units, each having a 6stage counter (called the stepper, see Fig. 2), 3 input terminals (the stepper
input, direct input and clear input), and 6 output terminals for each stage
of the stepper. Each such stage s was associated with a fixed number ds by
5
manually setting decade switches, and with 1 to 5 decade counters. If a pulse
arrived at the stepper input (SI) of a stepper, one was added to the counter of
stage s. If this number equaled the preset number ds ,it cleared the counter of
stage s and cycled to the next stage s + 1. In both cases, a program pulse was
emitted through the output terminal of stage s. A pulse at the direct input (DI)
immediately cycles the stepper to the next stage and a pulse at the clear input
(CI) resets the stepper to its initial configuration. In neither case a program
pulse was emitted. In this way the master programmer could be used, among
other things, to sequence operations and to iterate a given subroutine.
S6
S5
S4
S3
S2
S1
DISICI
Figure 2: A Schematic (Reduced) Representation of a stepper counter of the
Master Programmer. S1 to S6 represent the 6 stages of the stepper, DI is the
direct input, SI the stepper input and CI the clear input. Note that there are 3
input terminals for CI and DI and only 2 for SI.
2.4
Conditional Branching
The ENIAC was capable of discriminating between program sequences by examining the magnitude of some numerical result. This was done by using the
technique of converting digit pulses into program pulses, through special adaptors and dummy controls. “Magnitude discrimination” or “branching” could
be done by exploiting the fact that 9 digit pulses were transmitted for sign
indication M and none for sign indication P . The fact that digit pulses were
6
transmitted for every digit except for 0 could be exploited in a similar manner.
The digit pulse corresponding to the sign (or a digit) was converted into a program pulse by connecting the P M lead (or another digit lead) of the A and/or
S output terminal of an accumulator to a program cable. A special adaptor
which was placed on this lead took care of the conversion. The program cable
was then connected with the program pulse input terminal of (an) otherwise
unused “dummy (program) control(s)” (d.p.c.) [5, Sec. 4.5].5 There were two
main branching methods: the first is explained in detail by Adèle Goldstine [5],
the second one is only referred to without further explanation. In what follows
we will reconstruct both methods.
2.4.1
First Branching Method
A S
(i)
(ii)(iii)
o
AS
1
o o
0 0
1 1
P0
P1
P2
P3
P4
s.
a.
s.
a.
a
b
Figure 3: First Mode of Branching on the ENIAC
Fig. 3 illustrates the wiring of a branching method that uses both output
terminals. Assume that the discrimination is of the following nature: if a given
number n becomes smaller than some other number m, then program a should
be executed, else program b should be executed. If the accumulator is activated
to execute this kind of “magnitude discrimination” at time r, it should store
n − m (or m − n) at time r. For this method a first transceiver (i) should be
set such that the accumulator will send its content once through both its A and
S output channels, when (i) is stimulated by a program pulse. Now, in Fig.
3, when the discrimination program is activated at time r through program
cable P0, n − m is sent through the A and S output channels. If n < m, a
negative number is transmitted, nine pulses are transmitted through the PM
lead of the A output terminal and no pulse is transmitted at the PM lead of
5 A dummy control had the following main functions [5, Sec. 4.5]: “The dummy program
has at least 3 important functions: (1) conversion of digit pulses into program pulses, (2)
delay of a program pulse, and (3) isolation of programs from one another.”
7
the S output terminal. The nine pulses transmitted through the PM lead of the
A output terminal are converted into a program pulse using a special adaptor
(indicated as “s.a.” in Fig. 3), thus stimulating program cable P1 which in its
turn activates the first dummy control (transceiver (ii)) at time r + 1. At time
r + 2 a program pulse is transmitted from (ii) leading to the execution of some
program a. Similarly, program b will be activated when n ≥ m.6
2.4.2
Second Branching Method
Fig. 4 shows a branching method that uses only one output terminal, together
with the master programmer. This method is preferred if one needs to be
economical with the accumulators, avoiding to use a complete accumulator only
for the execution of branching. One stepper si of the master programmer is
used: both the first and second stage of this stepper should be set to cycle to the
next stage when they reach some value v.7 As is the case for the first method,
if one wants to discriminate between two programs depending on whether a
number n becomes smaller than a certain value m, the accumulator doing the
discrimination should store n − m at the time of the discrimination. Assume
that the discrimination program is activated at time r through program cable
P0 in Fig. 4. This activates the transmission of n − m through the A output
terminal. Now let n ≥ m. Then, at time r + 1 the dummy control (transceiver
(ii)) will not be activated and transceiver (i) will send a program pulse to cable
P 1 activating another transceiver (iii) of the accumulator. This transceiver is
set to do nothing except for sending a program pulse when it is “done” at time
r + 2 to cable P4. This cable is connected to the stepper input of stepper si of
the master programmer. The program pulse arriving at the stepper input will
lead to the addition of 1 to the counter of stage 1. At time r + 3 a program
pulse is then sent from stage 1 of stepper si to program cable P5, activating
program a and also resetting the stepper.
If, on the other hand, n < m, then, at time r+1 a dummy control (transceiver
(ii)) and transceiver (iii) are activated, both set-up to do nothing except transmitting a program pulse at time r + 2 to program cable P3 rsp. P4. P3 is
connected to the direct input of stepper si , P4, as we already know, to the
stepper input of stepper si . This means that both the direct input as well as
the stepper input are stimulated simultaneously. This is unproblematic because
the result of a program pulse at the direct input is that the stepper immediately
cycles to the next stage of the stepper [5, Sec. 10.5]. The program pulse at the
stepper input then results in the addition of 1 to the counter of stage 2. At time
r + 3 a program pulse is sent from stage 2 of stepper si to program cable P6,
resulting in the activation of program b as well as the resetting of stepper si .
It should be pointed out that in the reconstruction of Sec. 3.3 we will only
use the second branching method. There we will use the simplified and reduced
6 Note that the case n = m is processed in the same way as n > m.
7 If the stages of stepper s are used only for branching, the value of v is irrelevant so v may
i
be set to 1. However, it may be useful to re-use these stages of the stepper at some earlier or
later stage of a given program in which specific values v need to be set.
8
PM
A S
s.
a.
(i)
(ii) (iii)
o
A
1
o o
0 0
1 1
v
v
P0
P1
P2
P3
P4
P5
P6
a
b
Figure 4: Second Mode of Branching on the ENIAC
PM
scheme of Fig. 5 to keep the structure of the wiring schemes transparent.
A S
s.
a.
(i)
(ii)(iii)
o
A
1
o o
0 0
1 1
P0
P1
P2
P3
a
b
Figure 5: Simplified Representation of the Second Mode of Branching on the
ENIAC
2.5
Programming ‘primitives’
As has been acknowledged by Prespert J. Eckert, the head engineer of the
ENIAC, the main inspiration for the ENIAC was the idea of “linked adding
machines” [16, p. 22]. This origin is still visible in the architecture of the ENIAC,
especially in the crucial role of the accumulators. However, the designers added
a powerful set of “programming” machinery, like conditional branching and the
master programmer. Patching together a sequence of different processes into
9
one program that could be run without interruption was thus possible (within
the limits of the hardware), though all but a straightforward task.
As a consequence, it need not wonder that among the first users of the
ENIAC, many conceived aids and simplifications to program the machine. Since
looping and sequencing could be controlled by the master programmer, a scheme
of the wiring on the master programmer was used to represent the sequencing of
and interconnections between the main steps of the computation. This kind of
“reduced” wiring scheme was used by D.R. Hartree in an article that described
his ENIAC computation [17].8
H.H. Goldstine and J. von Neumann developed a further abstraction from
such a scheme and from the actual hardware in the context of the EDVAC
project [18]. Their flow diagrams were used “to plan first the course of the
process and the relationship of its successive stages to their changing codes,
and to extract from this the original coded sequence as a secondary operation.”
[18, p. 4]. Flow diagrams are flexible tools that have remained in use until today. Finally, the logician H.B. Curry developed an abstract calculus of program
composition using his theory of combinators in the process of studying how to
put an inverse interpolation problem on the ENIAC [19]. Curry’s work will,
however, be the topic of another paper, in the meantime we refer to Knuth and
Pardo’s lapidary discussion [20, pp. 434–435].
The most difficult kind of program on the ENIAC, even with the tools Goldstine, von Neumann and Curry had devised, remained the programming of a
complex parallel computation. In 1996, the historians Marcus and Akera proposed a modus procedendi for synchronizing such a parallel computation. For
the synchronization of two branches, they proposed to program the master programmer as follows [16, p. 23]:
1) Set the first stage of a stepper on 11.
2) Route a program pulse into the stepper, and let the stage of the
stepper provide a program pulse each addition time.
3) Route the program output pulse of the first branch into the direct
input of the 1st decade of the first stage, so that the counter goes
from 0 to 1 or from 10 to 11. Route the program output pulse of
the second branch into the direct input of the 2nd decade so that
the counter goes from 0 to 10 or from 10 to 11.
4) Connect the output of the second stage to whatever happens after
both parallel paths have terminated.
Since a stepper stage can be associated with maximally 5 decades, this procedure
would allow for a maximum of 5 parallel branches to be synchronized, i.e., each
branch contributing a “1” to a decade and the next step in the program only
beginning when the stage content has reached “11111”. According to Marcus
and Akera, this set-up was never used.
8 We will make use of this representation for Lehmer’s program below (See Fig. 7). Incidentally, Hartree thanks K. McNulty (one of the all-female team of ENIAC programmers)
and D.H. Lehmer for helping him set up and program the ENIAC.
10
There are, however, other ways of synchronizing parallel branches that have
been wired on the ENIAC. The second branching method (2.4.2) is actually
an example of synchronizing two branches. Moreover, Mauchly and Lehmer
synchronized a 14-branch parallel computation within Lehmer’s calculation of
exponents of 2 modulo p (the sieve).
3
Lehmer’s Program
In 1946 the number-theorist Derrick H. Lehmer and his wife Emma spent a
Fourth-of-July weekend testing the ENIAC. The Lehmer family – Derrick, his
wife Emma and two teenage kids – arrived at the Moore school on Friday 5 p.m.
where they met John Mauchly. Mauchly helped them set up the ENIAC for the
implementation of Lehmer’s program and stayed on as an operator through the
week-end [21, p. 451]. According to Akera [22, p. 40]
[Lehmer’s program] was a difficult enough problem that it attracted
the attention of some mathematicians who could say, yes, an electronic computer could actually do an interesting problem in number
theory – something as sophisticated in number theory – and produce
useful results. There were many people who speculated about this –
von Neumann among them – but to actually do it, to demonstrate
it, was, I think, important to the post-war reputation of electronic
computers among mathematicians.
The objective of Lehmer’s program was to compute a list of exponents e of 2
mod p, p prime.9
3.1
Description of the Problem
It was first noted by J.H. Lambert in 1770 that Fermat’s little theorem (ap−1 ≡
1 mod p) could be used as a primality test. If for a given number b, 2b ≡ 2 mod b
than b is with high probability a prime number. Unfortunately, an infinite set
of exceptions to this primality test exists.
Early on in his career, D.H. Lehmer had written on the converse of Fermat’s
theorem [23], and often returned to the topic. In 1936 Lehmer established the
general form of exceptions to the converse [24]. He proved that if for a composite
number n = pq (p and q prime), 2pq − 2 is divisible by pq, this can only occur if
and only if p − 1 is divisible by the exponent of 2 modulo q and q − 1 is divisible
by the exponent of 2 modulo p [24, p. 353]. Similar theorems can be proven for
composites b of the forms pqr, pqrs etc. that satisfy 2b ≡ 2 mod b. It is thus
clear that a list of exponents of 2 mod p (p prime) is useful for the construction
of prime tables.
Lehmer had been using Kraı̂tchik’s tables for finding exponents of 2. These
tables, however, only extended to 300 000, and contained rather a lot of errors.
9 The exponent e of 2 modulo a prime p is defined as follows: the smallest number e such
that 2e ≡ 1 mod p.
11
As a result of his ENIAC computation Lehmer published a list of errors to
Kraı̂tchik’s tables [25] and a list of factors of 2n ± 1 [26], both in 1947. More
details on his actual computation were only published two years later in [27]
due to the “classified” label attached to many things involving the ENIAC.
In the following two sections, we will provide the details of the set-up of
Lehmer’s program on the ENIAC. The flow diagram and the algorithms as
presented in Sec. 3.2 follow the discussion by Lehmer himself [27]. The actual
reconstruction of Sec. 3.3 of the set-up is ours.
3.2
The Problem from the Machine’s Eye View
Translating a problem from man’s view to a machine was not obvious when
the first electronic computers emerged. Originally, this translation involved two
main steps: programming and coding. According to Hartree [28, pp. 111–112]:
“Programming” is the process of drawing up the schedule of the
sequence of individual operations required to carry out the calculation, and “coding” is the process of translating these operations
into instructions in the particular form in which they are read by
the machine.
Coding was clearly the more difficult part, especially for a machine such as the
original ENIAC where coding equalled wiring the machine. This was exactly the
reason for rewiring the ENIAC (Sec. 1, p. 2) and developing so-called “storedprogram logical machines”, i.e., the von Neumann architecture. The difficulties
of coding as well as the immense speed-up of electronic computing had an impact
on the more ‘high-level’ programming part. The approaches and algorithms
that had been in use for human computors and for desk calculators had to be
rethought in function of the restrictions that coding the machine imposed and
of the exponential speed-up of the ENIAC.10 In [27] Lehmer explicitly addressed
this translation problem in the discussion that accompanied the flow chart for
the ENIAC’s computation of the exponents e of 2 modulo p (See Fig. 6).
A human computer would calculate the exponents e more or less in the
following way. First, he would take a list of primes and select the next p.
Then he would, using the many abbreviations possible in modular arithmetic,
calculate powers of 2 and reduce them modulo p, though not all powers, but
only those that are divisors of p − 1. This is because a number-theorist knows
that if there is an exponent e (< p − 1) of 2 so that 2e ≡ 1 mod p (p prime),
than e is either a divisor of or equal to p − 1. He might also make use of already
existing tables of exponents.
As Lehmer later described it, “[i]n contrast, the ENIAC was instructed to
take an “idiot” approach” [30, p. 4]. A first problem is to let the machine
recognize the “next prime”:
10 In a test computation [29] the ENIAC computed a trajectory in 15 minutes, the second
best machine, Bell’s relay calculators, needed 70 hours!
12
The “next value of p” [i.e. the next prime] presents an interesting
problem to the ENIAC. [Circumstances] prevented the introduction
[of] punched cards. [...] This means that the ENIAC should somehow compute its own values of p. To this effect a “sieve” was set
up which screened out all numbers having a prime factor ≤ 47. [27,
p. 302]
A more sophisticated method would have required “much outside information
[introduced] via punched cards [...] to be prepared by hand in advance” [27,
p. 302]. Not only would this have taken quite some time, but it would have been
mandatory to synchronize the card reader with the rest of the computation using
the interlock, making the set-up considerably more difficult. Therefore, Lehmer
entered a sieve into the program to select the next p (box (III) in the flow chart,
Fig. 6). This sieve is a rather straightforward implementation of Eratosthenes’s
sieve, where multiples of already known primes p1 , p2 , ..., pn are calculated and
then rejected, leaving only those integers relatively prime to p1 , p2 , ..., pn . In the
ENIAC computation, only odd numbers were tested so 2 had not to be included
as a test prime in the sieve. The ENIAC checked in parallel whether a next
number p (in a progression of numbers to be checked p = 2i + 1) is a multiple
of one or more of the first 14 odd prime numbers (3 to 47) or not. About 86 %
of the composite integers were thus eliminated. To reduce the number of “fake”
primes remaining, Lehmer added an extra test later on in the program (box (V)
in the flow chart, Fig. 6).
Next, the powers of 2 were calculated and reduced modulo p (p being a
sieved number) to compute the exponents e in the following way [30, pp. 4–5]
(box (IV) in the flow chart, Fig. 6):
In contrast, the ENIAC was instructed to take an “idiot” approach,
based directly on the definition of e, namely, to compute
2n ≡ Γn (modp), n = 1, 2, ....
until the value 1 appears or until n = 2001, whichever happens first.
Of course, the procedure was done recursively by the algorithm:
Γn + Γn
if Γn + Γn < p
Γ1 = 2, Γn+1 =
Γn + Γn − p otherwise
Only in the second case can Γn+1 be equal to 1. Hence this delicate
exponential question in finding e(p) can be handled with only one
addition, subtraction, and discrimination at a time cost, practically
independent of p, of about 2 seconds per prime. This is less time
than it takes to copy down the value of p and in those days this was
sensational.
The limit value for e was changed two times during Lehmer’s three-day computation. For the first 300.000 integers e was ≤ 2000, for 300000 < p < 1000000
this limit was reduced to 1000 and for 106 < p < 4.5 · 106 it was set at 300 [27,
p. 302].
13
I. Initiation and Preliminary setup
II. Increase p by 2
III. Sieve
Is p divisible by a prime ≤ 47?
Yes
No
IV. Exponent Routine
Is e > 2000 ?
Yes
No
V. Does e divide p-1 ?
No
VI. Erase exponent
Calculation
Yes
VII. PRINT
p, e and f
Figure 6: Lehmer’s flow chart for the ENIAC computation
14
Finally, having obtained the exponent e of 2 modulo p, an extra primality test
on p was introduced (box (V) in the flow chart, Fig. 6). As known, p−1 must be
divisible by e if p is prime. A division by repeated subtraction checked whether
p − 1 was divisible by e, if so, the quotient f was stored (and later punched),
if not, the value p was rejected. After this final elimination of composite p’s,
only 25 composite numbers remained in the range 1000000 < p < 3000000.
Emma Lehmer finally eliminated these few remaining composites manually “by
comparison with [a] list of primes” [27, p. 302].
3.3
Reconstruction of the Program
In Fig. 7 Lehmer’s flow diagram is translated in a wiring scheme of the master
programmer, following Hartree’s representation of ENIAC programs (See Sec.
2.5). In what follows we will first give a brief informal account of the three main
steps of the program and then provide the details of the reconstruction.
The following conventions will be used in the reconstruction. First, it is
assumed that for every accumulator the transceivers are numbered as t1 , t2 , ..., t8
from left to right. Secondly, even though we will make frequent use of the
constant transmitter, it will not be included in the schemes because it would
take up too much space and is the less important part of the wirings. Instead,
we will indicate the number transmitted by the constant transmitter directly on
the relevant numerical cable. Furthermore, if the operation switch, the repeater
switch and the clear-correct switch of a given transceiver of an accumulator are
marked with “X”, this means that the transceiver has already been used in some
other part of the program.
3.3.1
Informal account of the three main steps
The three most important and interesting steps of the computation are the sieve
(III), the exponent routine (IV) and the division routine (IV).
The sieve is used to determine whether or not a given number p = 2i + 1 is
prime relative to the first 14 odd prime numbers. Lehmer used a sieve instead
of a look-up table because this was the more efficient method (see Sec. 3.2, p.
12). Now, the sieve uses hardware parallelism: in our reconstruction, 14 out of
the 20 accumulators (Ap1 , ..., Ap14 ) are used to check in parallel whether or not
the number p being processed is divisible by one of the 14 prime numbers.
The main idea behind the sieve reconstruction can best be explained as follows.
Remember that the sieve method is used here to search for numbers that are
not divisible by a given set of prime numbers pi , in this case, the first 14 odd
primes. The sieve can be represented by a matrix of k rows and infinitely many
columns. The infinite rows are periodic, the i-th row having a period of pi . The
value in the r-th column and the i-th row equals r modulo pi . The problem is
then to find those columns that contain no 0s. For instance, suppose we have a
sieve which selects numbers that are not divisible by 2, 3 and 5. Then we would
get the following sieve matrix:
15
n=
105 − 1
(II)
n=
1
(VI)
n=
2000
(IV)
(V)
(VII)
Init
(III)
Figure 7: The set-up of the Master Programmer for Lehmer’s ENIAC computation
16
1 0
1 2
1 2
1 0
0 1
3 4
1 0
2 0
0 1
1
1
2
0 1
2 0
3 4
0 1
1 2
0 1
0 1
0 1
2 3
...
...
...
In this example, the first number (except for 1) not divisible by 2, 3 and 5 is 7,
since the 7th column is the first one that contains no 0s.
Basically, what is done in the ENIAC sieve is that each of the 14 Apj corresponds to a row j of the sieve. At the start-up each accumulator stores the
complement of pj − 1.11 Now, since only odd numbers p are sieved, with every
loop of the sieve program each of the accumulators Apj is increased with 2 in
parallel. Thus, the first number p being checked is 3. Clearly, if any of the
numbers stored in one of the accumulators becomes 0 and thus positive, a divisor is detected (there is at least one 0 in the p-th column), if not, p is a prime
relative to each of the 14 primes. Dummy controls assure that, whatever the
case, the correct subroutine is followed. Of course, if an accumulator becomes
0, its content should be reset. I.e., the complement of 2pj should be loaded into
that accumulator. This is done by making use of the function tables and special
adaptors. Note that the mathematics of the sieve (addition and detection happen independently in each of the accumulators) guarantees that the 14 branches
are synchronous.
The program for the exponent routine was already described in detail in Sec.
3.2. It produces powers of 2 modulo the p found by the sieve. Besides the fact
that Lehmer chose for the “idiot” approach, he also had to economize on the
number of accumulators. For example, the doubling procedure uses only two
accumulators instead of the multiplier unit (that takes up four accumulators).
Also for the division routine, economization on ENIAC’s accumulators was necessary. Thus, Lehmer did not use the divider unit but wired his own division
procedure, basically an implementation of Euclid’s algorithm for integers. It
uses only 3 accumulators, of which two are also used in other subroutines. The
exponent e found is successively subtracted from the prime number p − 1 until
either p = 0 or p < 0.
Note that in this reconstruction full use is made of nearly all of ENIAC’s different components and salient features, including some of its special adaptors
and all of the accumulators. In this sense, the reconstructions highlight some of
ENIAC’s typical properties, possibilities and problems.
3.3.2
I. Initiation and preliminary set-up.
As will become clear throughout the remainder of this reconstruction, certain
components have to be set in advance. The 0th value (f (0)) of each of the function tables has to be set to a specific value. Also each of the 14 accumulators Apj
used in the sieve need to be set to the complement of pj −1 (See Sec. 3.3.4). The
numbers + 2, -2 and -1 are set manually on the constant transmitter (CT). An
accumulator AP used to store the number P = 2r + 1 being processed, should
be set to store the number 1. The computation is started by an initiating pulse
11 The reason for this is that the first p checked is equal to 3.
17
sent to the 14 accumulators and the CT.
3.3.3
II. Increase p by 2
In this subroutine the 14 Apj ’s (See Sec. 3.3.4), AP (storing the prime number p
being processed), as well as a transceiver Ae1 used in subroutine IV should be set
to receive once through one of their input channels. The constant transmitter
is set to send the value + 2 once. This leads to the desired increase of 2 in the
several components affected by this increase. Note that after the first execution
of II (the addition of 2), P = 3.
3.3.4
III Sieve
In our reconstruction of the prime sieve, we have used 14 accumulators Apj for
each prime pj ≤ 47, (1 ≤ j ≤ 14 ) except for 2.12 Upon initializa