Induction. The theorem is Example 6.1.2 in How To Prove It: 3 divides
n3 + 2n for every natural number n. In the package it
is Like_Example_6_1_2, in HTPILib/Chap6.lean, and the proof below
is Velleman's, copied without changes.
Two things make this the right example. First, the induction step needs a genuine algebraic
identity, so you get to see how Lean handles routine algebra (it does it for you, with
ring). Second, the divisibility is a hidden existential, so Chapter 3 shows up
again inside Chapter 6, which is the honest structure of the subject.
by_induc does
A goal of the form ∀ (n : Nat), P n is a universally quantified statement, so
you could try fix n : Nat and prove P n directly. Usually you
cannot. by_induc is the alternative: it replaces the single goal with
two goals.
⊢ ∀ (n : Nat), 3 ∣ n ^ 3 + 2 * n
by_induc: goal 1, the base case
⊢ 3 ∣ 0 ^ 3 + 2 * 0
by_induc: goal 2, the induction step
⊢ ∀ (n : Nat), 3 ∣ n ^ 3 + 2 * n → 3 ∣ (n + 1) ^ 3 + 2 * (n + 1)
These two states are reconstructed from the tactics that follow in Velleman's proof (they are forced by it), not quoted from his file.
That is the entire principle of mathematical induction, expressed as a single legal move. And look carefully at goal 2, because it answers the question every student asks in week eleven:
"Isn't assuming P(n) assuming what we're trying to prove?" No, and
here is the proof that it is not. The thing being proved is
∀ n, P n → P (n + 1). That is a statement about a conditional. Assuming
P n in order to derive P (n + 1) is just the ordinary Chapter 3
move for a conditional goal — the same assume you have been using since
September. The induction hypothesis is not an assumption of the theorem; it is the
antecedent of the implication you are currently proving. Lean's display makes this
impossible to misread, which is why doing one of these in Lean tends to settle the
question permanently.
The two goals are handled by the two · bullets in the proof below. Everything
indented under the first bullet is the base case; everything under the second is the
induction step. Nothing but indentation keeps them apart, so mind the whitespace.
Base case. 03 + 2·0 = 0 = 3·0, so 3 ∣ 0.
Induction step. Let n be arbitrary and suppose 3 ∣ n3 + 2n.
Then n3 + 2n = 3k for some k.
Now (n+1)3 + 2(n+1) = n3 + 2n + 3n2 + 3n + 3 = 3k + 3n2 + 3n + 3 = 3(k + n2 + n + 1),
so 3 ∣ (n+1)3 + 2(n+1).
By induction, the result holds for all n. □
theorem Like_Example_6_1_2 :
∀ (n : Nat), 3 ∣ n ^ 3 + 2 * n := by
by_induc
· -- Base Case
define --Goal : ∃ (c : Nat), 0 ^ 3 + 2 * 0 = 3 * c
apply Exists.intro 0
rfl
done
· -- Induction Step
fix n : Nat
assume ih : 3 ∣ n ^ 3 + 2 * n
define at ih --ih : ∃ (c : Nat), n ^ 3 + 2 * n = 3 * c
obtain (k : Nat) (h1 : n ^ 3 + 2 * n = 3 * k) from ih
define --Goal : ∃ (c : Nat), (n + 1) ^ 3 + 2 * (n + 1) = 3 * c
apply Exists.intro (k + n ^ 2 + n + 1)
show (n + 1) ^ 3 + 2 * (n + 1) = 3 * (k + n ^ 2 + n + 1) from
calc (n + 1) ^ 3 + 2 * (n + 1)
_ = n ^ 3 + 2 * n + 3 * n ^ 2 + 3 * n + 3 := by ring
_ = 3 * k + 3 * n ^ 2 + 3 * n + 3 := by rw [h1]
_ = 3 * (k + n ^ 2 + n + 1) := by ring
done
doneVerbatim from HTPILib/Chap6.lean (Velleman), his comments included.
define
English: "3 divides 0 means: there is a c with 0 = 3c."
What it does: unfolds ∣. Divisibility is a definition, and the
definition is existential. This is the moment Chapter 3 walks back into the room.
Goal : ∃ (c : Nat), 0 ^ 3 + 2 * 0 = 3 * c
apply Exists.intro 0 then rfl
Supply the witness — c = 0 — and then the remaining goal is
0 ^ 3 + 2 * 0 = 3 * 0, which both sides compute to 0.
rfl ("reflexivity") closes any goal whose two sides are literally the same
after computation. For concrete numbers, Lean just does the arithmetic.
fix n : Nat and assume ih : 3 ∣ n ^ 3 + 2 * n
The goal was ∀ n, P n → P (n + 1). Universal goal, so fix;
conditional goal, so assume. There is nothing induction-specific happening
here at all, which is the point made in the box above. The name ih is a
convention, not a keyword — you could call it h7.
n : Nat ih : 3 ∣ n ^ 3 + 2 * n ⊢ 3 ∣ (n + 1) ^ 3 + 2 * (n + 1)
Reconstructed from the surrounding tactics, not quoted from Velleman's file.
define at ih and obtain (k : Nat) (h1 : ...) from ih
English: "So n3 + 2n = 3k for some k."
What it does: unfold the divisibility in the hypothesis, exposing an existential;
then obtain strips the existential and hands you a name for the witness plus
the fact about it. This is the Chapter 3 move for an existential given, and it is
the reason the divisibility exercises are a good first induction: you get to use both
halves of Chapter 3's existential machinery in a single proof.
obtain
ih : ∃ (c : Nat), n ^ 3 + 2 * n = 3 * c k : Nat h1 : n ^ 3 + 2 * n = 3 * k
define and apply Exists.intro (k + n ^ 2 + n + 1)Unfold the divisibility in the goal, then supply the witness. And here is the actual mathematical content of the whole proof: you have to know what c is before you can start computing.
Goal : ∃ (c : Nat), (n + 1) ^ 3 + 2 * (n + 1) = 3 * c
On paper you discover c = k + n2 + n + 1 by doing the algebra and reading off the answer at the end. Lean makes you commit to it first, then verify. That is a real difference in workflow and it is worth naming: do the scratch algebra on paper, then write the Lean proof knowing the answer. Trying to discover the witness while inside Lean is miserable and is not what the tool is for.
calc block
The chain of equalities, one line per equals sign, with a justification after each
:=. The underscore means "the right-hand side of the line above."
calc (n + 1) ^ 3 + 2 * (n + 1)
_ = n ^ 3 + 2 * n + 3 * n ^ 2 + 3 * n + 3 := by ring
_ = 3 * k + 3 * n ^ 2 + 3 * n + 3 := by rw [h1]
_ = 3 * (k + n ^ 2 + n + 1) := by ringVerbatim excerpt from HTPILib/Chap6.lean (Velleman).
Two justifications appear. ring proves any identity that holds in every
commutative ring by normalizing both sides — expanding
(n + 1) ^ 3, collecting terms, all of it. You do not check that step and you
do not have to. rw [h1] substitutes using the induction hypothesis,
replacing n ^ 3 + 2 * n by 3 * k.
Notice the shape: algebra, then the induction hypothesis, then algebra. The middle line is the only one that uses induction at all, and the outer two are bookkeeping that the machine does. That decomposition is invisible in the paper proof, where the whole computation is one displayed chain, and seeing it separated is genuinely clarifying about what induction contributes.
Two more of Velleman's, verbatim, because the other two things Chapter 6 asks you to do by induction are sums and inequalities, and each has one wrinkle.
theorem Like_Example_6_1_1 :
∀ (n : Nat), (Sum i from 0 to n, 2 ^ i) + 1 = 2 ^ (n + 1) := by
by_induc
· -- Base Case
rewrite [sum_base]
rfl
done
· -- Induction Step
fix n : Nat
assume ih : (Sum i from 0 to n, 2 ^ i) + 1 = 2 ^ (n + 1)
show (Sum i from 0 to n + 1, 2 ^ i) + 1 = 2 ^ (n + 1 + 1) from
calc (Sum i from 0 to n + 1, 2 ^ i) + 1
_ = (Sum i from 0 to n, 2 ^ i) + 2 ^ (n + 1) + 1 := by
rw [sum_from_zero_step]
_ = (Sum i from 0 to n, 2 ^ i) + 1 + 2 ^ (n + 1) := by ring
_ = 2 ^ (n + 1) + 2 ^ (n + 1) := by rw [ih]
_ = 2 ^ (n + 1 + 1) := by ring
done
doneVerbatim from HTPILib/Chap6.lean (Velleman).
The wrinkle is that Sum i from 0 to n, f i is a defined notation, and peeling
one term off the end is a theorem, not a manipulation. That theorem is
sum_from_zero_step, and it says exactly what you would want:
theorem sum_from_zero_step {A : Type} [AddZeroClass A] {n : Nat} {f : Nat → A} :
Sum i from 0 to (n + 1), f i = (Sum i from 0 to n, f i) + f (n + 1)Verbatim from the header comment of HTPILib/Chap6.lean (Velleman); the theorem itself lives in Chap8Part1.
sum_base is its companion for the one-term sum. On paper, "… the sum up
to n+1 is the sum up to n plus the last term" is a sentence you write without
thinking. In Lean it is a named lemma you rewrite with. This is a fair summary of the whole
experience of formalizing: the steps are the same steps, and the ones you never noticed
turn out to have names.
theorem Example_6_1_3 : ∀ n ≥ 5, 2 ^ n > n ^ 2 := by
by_induc
· -- Base Case
decide
done
· -- Induction Step
fix n : Nat
assume h1 : n ≥ 5
assume ih : 2 ^ n > n ^ 2
have h2 : n * n ≥ 5 * n := Nat.mul_le_mul_right n h1
show 2 ^ (n + 1) > (n + 1) ^ 2 from
calc 2 ^ (n + 1)
_ = 2 * 2 ^ n := by ring
_ > 2 * n ^ 2 := by linarith
_ ≥ n ^ 2 + 5 * n := by linarith
_ > n ^ 2 + 2 * n + 1 := by linarith
_ = (n + 1) ^ 2 := by ring
done
doneVerbatim from HTPILib/Chap6.lean (Velleman).
Three things worth naming.
by_induc works
that out from the ∀ n ≥ 5 in the statement. The induction step accordingly
gets an extra hypothesis, h1 : n ≥ 5, which you will need.
decide closes the base case. It means "this is a decidable claim
about concrete numbers — compute it." Here that is 32 > 25.
calc chain can mix =, > and
≥, and Lean composes the relations correctly to conclude
> overall. linarith discharges each inequality step: it is a
decision procedure for linear arithmetic over ordered fields, and it will use anything in
the context, which is how h2 and ih get used without being
mentioned.
ring, linarith and decide are standard Lean, not
Velleman's dialect. They are the first genuine automation you meet, and they are the
reason formalizing Chapter 6 is less tedious than formalizing Chapter 3.
In Chap6Ex.lean, Section 6.1:
theorem Exercise_6_1_9a : ∀ (n : Nat), 2 ∣ n ^ 2 + n := sorryVerbatim from Chap6Ex.lean (Velleman).
This is Like_Example_6_1_2 with different numbers, so you can follow that
proof structurally, line for line. Do it on paper first — in particular, work out the
witness c before you type anything.
Then, harder and more interesting:
theorem Like_Exercise_6_1_4 :
∀ (n : Nat), Sum i from 0 to n, 2 * i + 1 = (n + 1) ^ 2 := sorry
theorem Exercise_6_1_15 : ∀ n ≥ 10, 2 ^ n > n ^ 3 := sorryVerbatim from Chap6Ex.lean (Velleman).
The first follows Like_Example_6_1_1; the second follows
Example_6_1_3, and is a genuine fight, mostly because finding the right
intermediate inequality is genuine work. That is fine. It is supposed to be.
theorem Exercise_6_1_9a : ∀ (n : Nat), 2 ∣ n ^ 2 + n := by
by_induc
· -- Base Case
define
apply Exists.intro 0
rfl
done
· -- Induction Step
fix n : Nat
assume ih : 2 ∣ n ^ 2 + n
define at ih
obtain (k : Nat) (h1 : n ^ 2 + n = 2 * k) from ih
define
apply Exists.intro (k + n + 1)
show (n + 1) ^ 2 + (n + 1) = 2 * (k + n + 1) from
calc (n + 1) ^ 2 + (n + 1)
_ = n ^ 2 + n + 2 * n + 2 := by ring
_ = 2 * k + 2 * n + 2 := by rw [h1]
_ = 2 * (k + n + 1) := by ring
done
doneStatement verbatim from Chap6Ex.lean; proof written for this page (not machine-checked). It is Like_Example_6_1_2 with the numbers changed — check it in Lean rather than believing this page.
If you have got this far and want more, the natural next steps are Chapter 6's later
sections — strong induction and recursion, where by_strong_induc and
recursive definitions like fact and Fib live — and then
Chapter 7, number theory, which is where the exercises stop feeling like exercises.
If instead you want to leave the dialect and use Lean for real mathematics, read the Transitioning to Standard Lean section of the appendix, then look at Mathlib. That is a different and much larger world, and nothing in this course is preparation for it beyond the one thing that matters: knowing what a goal state is and how to look at one.