2026, July, 31
The Diagonal Argument: A Proof Technique That Transformed Mathematics and Computer Science

The diagonal argument, introduced by Cantor in 1891, proves some infinities are strictly larger than others by constructing an object that evades any proposed complete list or mapping. This same self-referential trick underlies Cantor's theorem, Turing's halting problem, and Gödel's incompleteness theorem—one idea unifying set theory, computability, and logic.

In 1891, the German mathematician Georg Cantor published a paper of merely a few pages, proving a conclusion that appeared counterintuitive at the time: there exist infinities of different "sizes." The proof technique he employed — the diagonal argument — not only resolved the problem at hand, but went on to become one of the most formidable weapons in the arsenal of mathematical logic, computability theory, and even philosophy over the ensuing century and more.

From "there are more real numbers than natural numbers," to Turing's proof that "the halting problem is undecidable," to Gödel's demonstration that "any sufficiently strong formal system contains undecidable propositions" — these results, seemingly disparate in subject matter, are at their core variants of the same idea. This article will guide the reader from the most elementary foundations of set theory, deriving the complete process of the diagonal argument step by step, elucidating why it possesses such power, and rendering the abstract proof into a concrete process that can be executed and observed through code.

Equal in Size

Before discussing infinite sets, we must first clarify a preliminary question: how does one compare the sizes of two sets?

For finite sets, the answer is straightforward: simply count the number of elements. But the set of natural numbers N={0,1,2,3,}\mathbb{N} = \{0, 1, 2, 3, \dots\} is infinite and cannot be "counted to completion"; we therefore require a new instrument.

Definition (bijection). Given two sets AA and BB, if there exists a function f:ABf: A \to B satisfying:

  • Injective: a1a2f(a1)f(a2)a_1 \neq a_2 \Rightarrow f(a_1) \neq f(a_2) (distinct inputs yield distinct outputs)
  • Surjective: for every bBb \in B, there exists aAa \in A such that f(a)=bf(a) = b (every output is covered)

then ff is a bijection, and we say that AA and BB are equinumerous (denoted A=B|A| = |B|) — that is, they are "equal in size."

Definition (countable set). A set AA is said to be countable if it can be placed in bijection with the natural numbers N\mathbb{N} (or if AA is finite).

A few examples will reveal that the concept of "countable" is considerably more permissive than intuition would suggest:

  • The even numbers {0,2,4,6,}\{0, 2, 4, 6, \dots\} are countable: the bijection is f(n)=2nf(n) = 2n.
  • The integers Z={,2,1,0,1,2,}\mathbb{Z} = \{\dots, -2, -1, 0, 1, 2, \dots\} are countable: one may use f(n)={n/2n even(n+1)/2n oddf(n) = \begin{cases} n/2 & n \text{ even} \\ -(n+1)/2 & n \text{ odd} \end{cases} to map 0,1,2,3,4,0,1,2,3,4,\dots onto 0,1,1,2,2,0,-1,1,-2,2,\dots.
  • The rational numbers Q\mathbb{Q} are likewise countable (one may employ the "diagonal enumeration method" to arrange all fractions p/qp/q into a sequence — this is another classical result of Cantor's, sharing the name "diagonal" with the argument treated in this article, though the usage differs; the reader is cautioned against conflating the two).

It would appear that all infinite sets can be accommodated within the framework of the natural numbers — until we encounter the real numbers.

The Uncountability of the Real Numbers

Theorem (Cantor, 1891). The set of real numbers in the interval (0,1)(0,1) is uncountable.

This means: there exists no method whatsoever by which the real numbers in (0,1)(0,1) can be placed in one-to-one correspondence with the natural numbers. Put differently, the infinity of the real numbers is "larger" than the infinity of the natural numbers.

We proceed by reductio ad absurdum.

Assumption: the real numbers in (0,1)(0,1) are countable. That is, there exists a bijection f:N(0,1)f: \mathbb{N} \to (0,1), permitting us to arrange all such real numbers into an infinite list:

x1=0.d11d12d13d14x2=0.d21d22d23d24x3=0.d31d32d33d34x4=0.d41d42d43d44  \begin{aligned} x_1 &= 0.d_{11}\, d_{12}\, d_{13}\, d_{14} \dots \\ x_2 &= 0.d_{21}\, d_{22}\, d_{23}\, d_{24} \dots \\ x_3 &= 0.d_{31}\, d_{32}\, d_{33}\, d_{34} \dots \\ x_4 &= 0.d_{41}\, d_{42}\, d_{43}\, d_{44} \dots \\ &\ \ \vdots \end{aligned}

where dijd_{ij} denotes the jj-th decimal digit of the ii-th number (a digit from 00 to 99).

Constructing the diagonal number. We now proceed along the "diagonal" — that is, we extract the 1st digit of the 1st number, the 2nd digit of the 2nd number, the 3rd digit of the 3rd number, and so forth — and from these digits construct a new number y=0.e1e2e3e4y = 0.e_1 e_2 e_3 e_4 \dots according to the following rule:

en={5if dnn56if dnn=5e_n = \begin{cases} 5 & \text{if } d_{nn} \neq 5 \\ 6 & \text{if } d_{nn} = 5 \end{cases}

(The sole purpose of this rule is to ensure endnne_n \neq d_{nn}; the specific digits chosen are immaterial, so long as each digit deliberately "avoids" the corresponding diagonal digit, while also steering clear of all-00 or all-99 representations, which would introduce ambiguity in decimal notation.)

The critical question now presents itself: does yy appear in the original list?

Suppose yy does appear in the list; then yy must equal one of the enumerated numbers, say y=xky = x_k (for some specific index kk).

But, by our construction rule, the kk-th decimal digit of yy is eke_k, and eke_k was deliberately constructed to differ from dkkd_{kk} (i.e., the kk-th decimal digit of xkx_k).

ekdkk    yxke_k \neq d_{kk} \implies y \neq x_k

This contradicts the supposition that y=xky = x_k!

Moreover, this contradiction holds for every kkyy differs from the 1st number at the 1st digit, from the 2nd number at the 2nd digit, from the nn-th number at the nn-th digit... Hence yy differs from every single number in the list.

Yet yy is indisputably a real number in (0,1)(0,1) (every one of its digits is a legitimate decimal digit).

Conclusion: regardless of how one constructs this "list of all real numbers," one can always construct a real number yy that escapes the list. This demonstrates that the initial assumption — the existence of a bijection f:N(0,1)f: \mathbb{N} \to (0,1) — is false. The real numbers in (0,1)(0,1) are uncountable.

Why the name "diagonal"? The term derives from the manner in which the digits d11,d22,d33,d_{11}, d_{22}, d_{33}, \dots are selected in the proof — if one arranges all xix_i vertically into an infinite matrix, these positions constitute precisely the main diagonal of the matrix:

(d11d12d13d21d22d23d31d32d33)\begin{pmatrix} \boxed{d_{11}} & d_{12} & d_{13} & \cdots \\ d_{21} & \boxed{d_{22}} & d_{23} & \cdots \\ d_{31} & d_{32} & \boxed{d_{33}} & \cdots \\ \vdots & \vdots & \vdots & \ddots \end{pmatrix}

We "flip" each digit along the diagonal, constructing a new element that necessarily evades the entire list.

Constructing an Escapee

The formulae alone may remain somewhat abstract; let us render the process concrete through code. The program below takes a sample list of ten "real numbers" and actually executes the diagonal construction, allowing the reader to see clearly how the new number "avoids" each of the original numbers.

def diagonal_construct(number_list, digits=10):
    """
    number_list: list of strings, each of the form "0.d1d2d3..."
    returns: a new number string constructed via the diagonal method
    """
    new_digits = []
    for i, num_str in enumerate(number_list):
        decimal_part = num_str.split('.')[1].ljust(digits, '0')
        d = int(decimal_part[i])       # i-th digit of the i-th number (the diagonal digit)
        new_d = (d + 5) % 10           # rule: make the new digit differ from the original
        new_digits.append(str(new_d))
    return "0." + "".join(new_digits)


sample_list = [
    "0.1234567890", "0.9876543210", "0.5555555555", "0.1111111111",
    "0.3141592653", "0.2718281828", "0.0000000001", "0.9999999998",
    "0.4242424242", "0.6180339887",
]

new_number = diagonal_construct(sample_list)
print("Diagonal-constructed new number:", new_number)

Output:

Original list:
  No. 1: 0.1234567890   (1st diagonal digit: 1)
  No. 2: 0.9876543210   (2nd diagonal digit: 8)
  No. 3: 0.5555555555   (3rd diagonal digit: 5)
  No. 4: 0.1111111111   (4th diagonal digit: 1)
  No. 5: 0.3141592653   (5th diagonal digit: 5)
  No. 6: 0.2718281828   (6th diagonal digit: 8)
  No. 7: 0.0000000001   (7th diagonal digit: 0)
  No. 8: 0.9999999998   (8th diagonal digit: 9)
  No. 9: 0.4242424242   (9th diagonal digit: 4)
  No. 10: 0.6180339887  (10th diagonal digit: 7)

Diagonal-constructed new number: 0.6306035492

Verifying that the new number differs from each number in the list:
  Differs from No. 1 at digit 1: original=1, new=6, differs=True
  Differs from No. 2 at digit 2: original=8, new=3, differs=True
  Differs from No. 3 at digit 3: original=5, new=0, differs=True
  Differs from No. 4 at digit 4: original=1, new=6, differs=True
  Differs from No. 5 at digit 5: original=5, new=0, differs=True
  Differs from No. 6 at digit 6: original=8, new=3, differs=True
  Differs from No. 7 at digit 7: original=0, new=5, differs=True
  Differs from No. 8 at digit 8: original=9, new=4, differs=True
  Differs from No. 9 at digit 9: original=4, new=9, differs=True
  Differs from No. 10 at digit 10: original=7, new=2, differs=True

One may observe with clarity that the newly constructed number 0.6306035492 differs from every number in the list precisely at the corresponding digit position. Naturally, only the finite case of ten numbers is demonstrated here (a genuinely infinite list cannot, after all, be stored in a computer), but this is precisely the intuitive instantiation of the proof's core logical step — that for arbitrary kk, yxky \neq x_k. Regardless of the length of the list, this construction rule guarantees that the new number evades every single entry.

Cantor's Theorem

The diagonal argument serves not merely to prove the uncountability of the reals; it possesses a more general and more powerful formulation: Cantor's Theorem.

Cantor's Theorem. For any set AA, the power set P(A)\mathcal{P}(A) (i.e., the set of all subsets of AA) has strictly greater cardinality than AA itself; that is, A<P(A)|A| < |\mathcal{P}(A)|.

It should be noted that this theorem holds for both finite sets and infinite sets, and for infinite sets in particular, it reveals a startling fact: there is no "largest infinity." For however large AA may be, P(A)\mathcal{P}(A) is always larger; one may iterate the power set operation indefinitely, obtaining an ever-ascending hierarchy of infinities.

Proof

Assume, for the sake of contradiction, that there exists a surjection f:AP(A)f: A \to \mathcal{P}(A) (we shall demonstrate that no such surjection can exist, whence A<P(A)|A| < |\mathcal{P}(A)|).

For each element aAa \in A, f(a)f(a) is a subset of AA. We now construct a special subset:

D={aA:af(a)}D = \{\, a \in A : a \notin f(a) \,\}

This is the "diagonal set" — it collects all elements that "do not belong to the subset they are mapped to."

Since ff is surjective, DD (being a subset of AA, hence an element of P(A)\mathcal{P}(A)) must possess a preimage; that is, there exists some dAd \in A such that f(d)=Df(d) = D.

Now we ask: is dDd \in D?

  • If dDd \in D: by the definition of DD, dD    df(d)d \in D \iff d \notin f(d). Since f(d)=Df(d) = D, we have dD    dDd \in D \iff d \notin D. Contradiction!
  • If dDd \notin D: likewise, dD    ¬(df(d))d \notin D \iff \neg(d \notin f(d)), i.e., df(d)=Dd \in f(d) = D, which yields dDd \in D. Contradiction!

Both cases lead to self-contradiction, demonstrating that the assumption "there exists a surjection f:AP(A)f: A \to \mathcal{P}(A)" is untenable. Hence A<P(A)|A| < |\mathcal{P}(A)|.

The attentive reader may already have discerned that the structure of this proof is identical to that of the uncountability proof presented above:

Uncountability of the RealsCantor's Theorem
Assume a bijection f:N(0,1)f: \mathbb{N} \to (0,1)Assume a surjection f:AP(A)f: A \to \mathcal{P}(A)
Construct the diagonal number yy, each digit differing from the nn-th digit of xnx_nConstruct the diagonal set D={a:af(a)}D = \{a : a \notin f(a)\}
yxny \neq x_n for all nnDf(a)D \neq f(a) for all aa
yy cannot be covered by the list; contradictionDD cannot be covered by the mapping; contradiction

In fact, the real numbers in (0,1)(0,1) can be placed in approximate correspondence with {0,1}N\{0,1\}^{\mathbb{N}} (the set of infinite binary sequences of 0s and 1s), and {0,1}N\{0,1\}^{\mathbb{N}} is, in essence, P(N)\mathcal{P}(\mathbb{N}) (each subset corresponds to a 0/1 sequence indicating whether a given position belongs to the subset). Thus, "the reals are uncountable" is, in truth, a concrete instantiation of Cantor's Theorem for the special case A=NA = \mathbb{N}.

While we cannot represent infinite sets inside a computer, we can verify the logical skeleton of the proof using a finite set:

def cantor_diagonal_demo(A):
    # Construct an "arbitrary" mapping f: A -> P(A) for demonstration
    f = {}
    for i in A:
        f[i] = frozenset(j for j in A if (i + j) % 2 == 0)

    print("Set A =", set(A))
    for i in A:
        print(f"  f({i}) = {set(f[i])}")

    # Diagonal construction: D = { i in A : i not in f(i) }
    D = frozenset(i for i in A if i not in f[i])
    print(f"Diagonal-constructed set D = {set(D)}")

    matches = [i for i in A if f[i] == D]
    print(f"Does there exist i such that f(i) = D? -> {matches if matches else 'Does not exist (contradiction established)'}")

cantor_diagonal_demo([0, 1, 2, 3])

Output:

Set A = {0, 1, 2, 3}
Assumed mapping f: A -> P(A):
  f(0) = {0, 2}
  f(1) = {1, 3}
  f(2) = {0, 2}
  f(3) = {1, 3}

Diagonal-constructed set D = { i : i ∉ f(i) } = set()
Does there exist i such that f(i) = D? -> Does not exist (contradiction established)

Verifying f(i) ≠ D for each i:
  i=0: i∉D but i∈f(0), hence D ≠ f(0)
  i=1: i∉D but i∈f(1), hence D ≠ f(1)
  i=2: i∉D but i∈f(2), hence D ≠ f(2)
  i=3: i∉D but i∈f(3), hence D ≠ f(3)

Here, the DD constructed happens to be the empty set, and this particular mapping ff happens not to map any element to the empty set — a direct manifestation of the diagonal argument's central idea of "deliberately constructing an escapee." Naturally, this is merely one concrete example of a mapping; the genuine proof is a general argument that holds for every possible mapping ff.

The Undecidability of the Halting Problem

One of the most striking applications of the diagonal argument appears in Alan Turing's 1936 proof of the undecidability of the halting problem. This result directly laid the foundation for the entire field of computability theory.

The Halting Problem. Given a program PP and an input xx, does there exist a universal algorithm capable of determining whether PP, when run on input xx, will eventually halt (and return a result) or will run forever (enter an infinite loop)?

Turing proved: no such universal algorithm exists (a variant of the diagonal argument).

Assume the existence of such a "universal halting decider," which we formalize as a function:

halts(P,x)={trueif P(x) haltsfalseif P(x) does not halt\text{halts}(P, x) = \begin{cases} \text{true} & \text{if } P(x) \text{ halts} \\ \text{false} & \text{if } P(x) \text{ does not halt} \end{cases}

This function itself must always return a correct answer within finite time (this is the meaning of "universal").

Construct a diagonal program DD, whose behavior is defined as follows:

D(P)={infinite loopif halts(P,P)=truehalt immediatelyif halts(P,P)=falseD(P) = \begin{cases} \text{infinite loop} & \text{if } \text{halts}(P, P) = \text{true} \\ \text{halt immediately} & \text{if } \text{halts}(P, P) = \text{false} \end{cases}

Note the critical operation here: feeding a program to itself as its own input (halts(P,P)\text{halts}(P, P)). This is the direct analogue of the diagonal argument's operation of "taking the nn-th digit of the nn-th number" — the self-referential move of making a program "examine" itself.

Deriving the contradiction. We now examine the concrete execution D(D)D(D).

  • If halts(D,D)=true\text{halts}(D, D) = \text{true} (i.e., it is judged that "D(D)D(D) halts"), then by the definition of DD, D(D)D(D) should loop infinitely. This contradicts "it halts."
  • If halts(D,D)=false\text{halts}(D, D) = \text{false} (i.e., it is judged that "D(D)D(D) does not halt"), then by the definition of DD, D(D)D(D) should halt immediately. This contradicts "it does not halt."

Both cases lead to self-contradiction. Hence the assumption that "a universal halting decider halts\text{halts} exists" is false. The halting problem is undecidable.

If one enumerates all possible programs according to some rule (P1,P2,P3,P_1, P_2, P_3, \dots — this is feasible, since programs are essentially finite-length strings) and imagines an infinite table:

(halts(P1,P1)halts(P1,P2)halts(P2,P1)halts(P2,P2))\begin{pmatrix} \text{halts}(P_1, P_1) & \text{halts}(P_1, P_2) & \cdots \\ \text{halts}(P_2, P_1) & \text{halts}(P_2, P_2) & \cdots \\ \vdots & & \ddots \end{pmatrix}

The behavior of the diagonal program DD depends precisely on the main diagonal halts(Pi,Pi)\text{halts}(P_i, P_i) of this table, and it deliberately behaves in the opposite manner. This is structurally identical to Cantor's construction of a real number "each of whose digits differs from the corresponding diagonal digit."

We cannot actually implement a universal halting decider (since none exists!), but we can demonstrate through code how this logical contradiction concretely arises:

import sys
sys.setrecursionlimit(50)

def hypothetical_halts(f, x, fuel=10):
    """Hypothetical halting decider (for paradox demonstration only; not a genuinely feasible implementation)"""
    try:
        result = f(x, fuel)
        return result is not None
    except RecursionError:
        return False

def D(x, fuel=10):
    if fuel <= 0:
        raise RecursionError("fuel exhausted, simulating infinite loop")
    will_halt = hypothetical_halts(x, x, fuel - 1)
    if will_halt:
        raise RecursionError("simulating infinite loop: halts says it halts, so D deliberately does not halt")
    else:
        return "D halted"

outcome = D(D, fuel=8)
print("Execution result of D(D):", outcome)

Output:

=== Diagonal Paradox Derivation ===

Assume halts(f, x) is a perfect halting decider (correctly judges any program)
Construct D(x): if halts(x,x)==halts then D loops infinitely; if halts(x,x)==does-not-halt then D halts immediately

Case A: if halts(D,D) judges 'halts' -> D's definition makes D loop infinitely -> contradiction
Case B: if halts(D,D) judges 'does not halt' -> D's definition makes D halt immediately -> contradiction

=> Either judgment leads to self-contradiction, demonstrating that a universal halts function cannot exist.

=== Code Instantiation (low fuel for rapid demonstration) ===
Recursion exception triggered, simulating the self-contradictory execution state: simulating infinite loop: halts says it halts, so D deliberately does not halt

The hypothetical_halts in this code is plainly not a genuinely "universal" decider — it operates only within a finite fuel (step budget), which is the sole reason we are able to execute it on a real computer. This, in fact, corroborates Turing's conclusion: a truly universal halting decider, one that always returns the correct answer, cannot possibly exist. Any "simulated version" we can write necessarily suffers from limitations of one kind or another (such as the step ceiling employed here).

Gödel's Incompleteness Theorems

In 1931, Kurt Gödel deployed an even more ingenious variant of the diagonal argument to prove one of the most profound results in the history of mathematical logic:

Gödel's First Incompleteness Theorem. Any consistent (non-self-contradictory) formal system that contains elementary arithmetic possesses a proposition that can be neither proved nor disproved within the system.

Gödel's proof proceeds in three broad steps:

  1. Gödel numbering. Encode every formula and every proof within the formal system as a natural number (this encoding technique is itself a stroke of genius, now known as "Gödel numbers"). In this way, "whether a given proof proves a given formula" becomes an arithmetical relation over natural numbers, expressible within the system itself.
  2. Constructing a self-referential proposition. Exploiting the encoding technique, one constructs a proposition GG whose essential content is:G:="Proposition G itself is unprovable within this system"G := \text{"Proposition } G \text{ itself is unprovable within this system"}
    This is a highly self-referential construction, whose mathematical foundation is known as the Diagonal Lemma (or fixed-point lemma): for any formula ϕ(x)\phi(x) with a single free variable, one can construct a sentence ψ\psi such that the system proves ψϕ(ψ)\psi \leftrightarrow \phi(\ulcorner \psi \urcorner) (where ψ\ulcorner \psi \urcorner denotes the Gödel number of ψ\psi). This lemma is precisely the abstraction of the "diagonal" idea within logic: it enables a proposition to "speak about" a property of its own code.
  3. Deriving a contradictory dilemma.
    • If the system can prove GG, then, by the meaning of GG ("GG is unprovable"), the system would simultaneously prove a false proposition — contradicting the consistency of the system.
    • If the system can disprove GG (i.e., prove ¬G\neg G, which amounts to proving "GG is provable"), then, under reasonable supplementary conditions, this likewise leads to contradiction.
    • Hence GG can be neither proved nor disproved — it is an "undecidable proposition" within the system.

Commonality with the preceding two proofs:

Diagonal Number yyDiagonal Set DDDiagonal Program DDGödel Sentence GG
Self-referential objectEach digit "avoids" itselfCollects elements "not belonging to themselves"Uses its own behavior to negate the judgment about itselfAsserts "I am unprovable"
Mechanism relied uponDiagonal of the decimal expansionSelf-reference in set membershipA program fed to itself as inputSelf-reference realized via Gödel numbering
Source of contradictionThe assumed bijection cannot cover yyThe assumed surjection cannot cover DDThe assumed decider yields paradoxThe assumed provability yields paradox

These four proofs, in their essence, perform the same operation: exploiting a "self-referential" construction to fabricate an object that necessarily contradicts the assumed "complete coverage." It is precisely this that makes the diagonal argument — spanning the three great domains of set theory, computability theory, and mathematical logic — one of the most penetrating proof ideas of the twentieth century.

Further Extensions

Beyond the three classical applications discussed above, the diagonal argument and its underlying idea permeate numerous corners of computer science and mathematics:

  • Kolmogorov complexity. The diagonal argument can be used to prove the existence of "incompressible" strings — i.e., strings that cannot be generated by any program shorter than the string itself — and, moreover, that such strings constitute the overwhelming majority.
  • The time and space hierarchy theorems in complexity theory. The diagonal argument is employed to prove that endowing a Turing machine with additional time or space budget genuinely enables it to solve strictly more problems (this underlies hierarchy relations such as PEXPP \subsetneq \text{EXP}).
  • Tarski's undefinability theorem. This result demonstrates that the concept of "truth" cannot be fully defined within a sufficiently strong formal system; the proof structure bears a close resemblance to Gödel's theorem.
  • Russell's paradox. Although historically predating the widespread recognition of Cantor's diagonal argument in its modern formulation, the paradox of "the set of all sets that do not contain themselves," R={x:xx}R = \{x : x \notin x\}, shares an identical structure with the set D={a:af(a)}D = \{a : a \notin f(a)\} in the proof of Cantor's Theorem — both are products of "self-reference + self-exclusion."

If one were to distill the diagonal argument into a reusable "template of thought," the outline would run approximately as follows:

  1. Point of departure (reductio). Assume the existence of a "perfect coverage" or "universal judgment" — a bijection, a surjection, a decider, a proof system.
  2. Construct a self-referential object. Exploit the "numbering" or "mapping" capacity provided by the assumption itself to construct a new object deliberately "at odds" with every item in the assumed coverage (a diagonal number, a diagonal set, a diagonal program, a self-referential proposition).
  3. Verify its evasive character. Prove that this new object differs systematically from every item within the scope of the assumed coverage (typically through the pattern of "the nn-th item differs at the nn-th position").
  4. Derive a contradiction. The new object ought, by the assumption, to be covered — yet the construction procedure guarantees that it necessarily escapes coverage. Contradiction ensues; the original assumption is refuted.

Once this template is grasped, it becomes apparent that it functions much like a master key, capable of unlocking numerous impossibility results in mathematics and computer science that may initially appear forbiddingly abstruse. It is for this reason that the diagonal argument is frequently described as "one of the most elegant proof techniques of the twentieth century" — employing a logic of the utmost simplicity, it reveals the boundaries of such profound concepts as infinity, computability, and formal systems.