Logical Foundations

9. IndProp: Inductively Defined Propositions🔗

Note to developers (before next release)

This chapter needs more (and better!) quizzes

import LF.Logic
import LF.CustomTactics
Note to developers (Mike Hicks @mwhicks1)

It would be convenient to declare the variables below so that inline prose throughout this chapter can use n, n', m, m', k, α, x, y, l, l₁, l₂, and l₃ without repeating their type annotations, but the same problem described in Logic applies: an unused variable is silently added to the local context in basically every proof from here on, even when the theorem never mentions it, which makes theorem hover-overs in the HTML book unusable. Until we have a way to declare variables visible only for inline prose (rather than for every lean block), we leave this commented out:

-- variable
--   (n n' m m' k : Nat)
--   (α : Type)
--   (x y : α)
--   (l l₁ l₂ l₃ : List α)

9.1. Inductively Defined Propositions🔗

In the Logic chapter, we looked at several ways of writing propositions, including conjunction, disjunction, and existential quantification.

In this chapter, we bring yet another new tool into the mix: inductively defined propositions.

To begin, some examples...

9.1.1. Example: The Collatz Conjecture🔗

The Collatz Conjecture is a famous open problem in number theory.

Its statement is quite simple. First, we define a function collatzStep on numbers as follows:

def div2 (n : Nat) : Nat := match n with | 0 => 0 | 1 => 0 | n' + 2 => div2 n' + 1 def collatzStep (n : Nat) : Nat := bif n.even then div2 n else (3 * n) + 1

Next, we look at what happens when we repeatedly apply collatzStep to some given starting number. For example, collatzStep 12 is 6, and collatzStep 6 is 3, so by repeatedly applying collatzStep we get the sequence 12, 6, 3, 10, 5, 16, 8, 4, 2, 1.

Similarly, if we start with 19, we get the longer sequence 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1.

Both of these sequences eventually reach 1. The question posed by Collatz was: Is the sequence starting from any positive natural number guaranteed to reach 1 eventually?

To formalize this question in Lean, we might try to define a recursive function that calculates the total number of steps that it takes for such a sequence to reach 1. You can write this definition in a standard programming language, but it is rejected by Lean's termination checker, since the argument to the recursive call, collatzStep n, is not "obviously smaller" than n.

def fail to show termination for reaches1In with errors failed to infer structural recursion: Cannot use parameter n: failed to eliminate recursive application reaches1In (collatzStep n) failed to prove termination, possible solutions: - Use `have`-expressions to prove the remaining goals - Use `termination_by` to specify a different well-founded relation - Use `decreasing_by` to specify your own tactic for discharging this kind of goal n:Nat⊢ collatzStep n < nreaches1In (n : Nat) : Nat := bif n == 1 then 0 else 1 + reaches1In (collatzStep n)
fail to show termination for
  reaches1In
with errors
failed to infer structural recursion:
Cannot use parameter n:
  failed to eliminate recursive application
    reaches1In (collatzStep n)


failed to prove termination, possible solutions:
  - Use `have`-expressions to prove the remaining goals
  - Use `termination_by` to specify a different well-founded relation
  - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
n:Nat⊢ collatzStep n < n

Indeed, this isn't just a pointless limitation: functions in Lean are required to be total, to ensure logical consistency.

Moreover, we can't fix it by devising a more clever termination checker: deciding whether this particular function is total would be equivalent to settling the Collatz conjecture!

Another idea could be to express the concept "eventually reaches 1 in the Collatz sequence" as a recursively defined property of numbers CollatzHoldsFor : Nat → Prop. This is also rejected by the termination checker. In principle, we could convince Lean that div2 n is smaller than n by supplying an appropriate proof. However, we still can't convince it that (3 * n) + 1 is smaller than n!

def fail to show termination for CollatzHoldsFor with errors failed to infer structural recursion: Cannot use parameter n: failed to eliminate recursive application CollatzHoldsFor (div2 n) failed to prove termination, possible solutions: - Use `have`-expressions to prove the remaining goals - Use `termination_by` to specify a different well-founded relation - Use `decreasing_by` to specify your own tactic for discharging this kind of goal n x✝:Nat⊢ div2 n < x✝CollatzHoldsFor (n : Nat) : Prop := match n with | 0 => False | 1 => True | _ => bif n.even then CollatzHoldsFor (div2 n) else CollatzHoldsFor ((3 * n) + 1)
fail to show termination for
  CollatzHoldsFor
with errors
failed to infer structural recursion:
Cannot use parameter n:
  failed to eliminate recursive application
    CollatzHoldsFor (div2 n)


failed to prove termination, possible solutions:
  - Use `have`-expressions to prove the remaining goals
  - Use `termination_by` to specify a different well-founded relation
  - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
n x✝:Nat⊢ div2 n < x✝

Fortunately, there is another way to do it: we can express the concept "reaches 1 eventually in the Collatz sequence" as an inductively defined property of numbers. Intuitively, this property is defined by a set of rules:

              ─────────────────── (one)
               CollatzHoldsFor 1

n.even = true     CollatzHoldsFor (div2 n)
─────────────────────────────────────────── (even)
               CollatzHoldsFor n

n.even = false    CollatzHoldsFor ((3 * n) + 1)
─────────────────────────────────────────────── (odd)
               CollatzHoldsFor n

So there are three ways to prove that a number n eventually reaches 1 in the Collatz sequence:

  • n is 1;

  • n is even and div2 n eventually reaches 1;

  • n is odd and (3 * n) + 1 eventually reaches 1.

We can prove that a number reaches 1 by constructing a (finite) derivation using these rules. For instance, here is the derivation proving that 12 reaches 1 (where we leave out the evenness/oddness premises):

─────────────────────── (one)
  CollatzHoldsFor 1
─────────────────────── (even)
  CollatzHoldsFor 2
─────────────────────── (even)
  CollatzHoldsFor 4
─────────────────────── (even)
  CollatzHoldsFor 8
─────────────────────── (even)
  CollatzHoldsFor 16
─────────────────────── (odd)
  CollatzHoldsFor 5
─────────────────────── (even)
  CollatzHoldsFor 10
─────────────────────── (odd)
  CollatzHoldsFor 3
─────────────────────── (even)
  CollatzHoldsFor 6
─────────────────────── (even)
  CollatzHoldsFor 12

Formally in Lean, the CollatzHoldsFor property is inductively defined:

inductive CollatzHoldsFor : Nat → Prop where | one : CollatzHoldsFor 1 | even {n : Nat} (h₁ : n.even = true) (h₂ : CollatzHoldsFor (div2 n)) : CollatzHoldsFor n | odd {n : Nat} (h₁ : n.even = false) (h₂ : CollatzHoldsFor ((3 * n) + 1)) : CollatzHoldsFor n

What we've done here is to use Lean's inductive definition mechanism to characterize the property "Collatz holds for..." by stating three different ways in which it can hold: (1) Collatz holds for 1, (2) if Collatz holds for div2 n and n is even, then Collatz holds for n, and (3) if Collatz holds for (3 * n) + 1 and n is odd, then Collatz holds for n. This Lean definition directly corresponds to the three rules we wrote informally above.

For particular numbers, we can now prove that the Collatz sequence reaches 1 (we'll look more closely at how it works a bit later in the chapter). Each step applies a rule and discharges the boolean evenness premise by rfl; the recursive premise is then reduced by the kernel from CollatzHoldsFor (div2 12) to CollatzHoldsFor 6, etc.

example : CollatzHoldsFor 12 := ⊢ CollatzHoldsFor 12 ⊢ Nat.even 12 = true⊢ CollatzHoldsFor (div2 12); ⊢ CollatzHoldsFor (div2 12) ⊢ (div2 12).even = true⊢ CollatzHoldsFor (div2 (div2 12)); ⊢ CollatzHoldsFor (div2 (div2 12)) ⊢ (div2 (div2 12)).even = false⊢ CollatzHoldsFor (3 * div2 (div2 12) + 1); ⊢ CollatzHoldsFor (3 * div2 (div2 12) + 1) ⊢ (3 * div2 (div2 12) + 1).even = true⊢ CollatzHoldsFor (div2 (3 * div2 (div2 12) + 1)); ⊢ CollatzHoldsFor (div2 (3 * div2 (div2 12) + 1)) ⊢ (div2 (3 * div2 (div2 12) + 1)).even = false⊢ CollatzHoldsFor (3 * div2 (3 * div2 (div2 12) + 1) + 1); ⊢ CollatzHoldsFor (3 * div2 (3 * div2 (div2 12) + 1) + 1) ⊢ (3 * div2 (3 * div2 (div2 12) + 1) + 1).even = true⊢ CollatzHoldsFor (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)); ⊢ CollatzHoldsFor (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)) ⊢ (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)).even = true⊢ CollatzHoldsFor (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))); ⊢ CollatzHoldsFor (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))) ⊢ (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))).even = true⊢ CollatzHoldsFor (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)))); ⊢ CollatzHoldsFor (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)))) ⊢ (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1)))).even = true⊢ CollatzHoldsFor (div2 (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))))); ⊢ CollatzHoldsFor (div2 (div2 (div2 (div2 (3 * div2 (3 * div2 (div2 12) + 1) + 1))))) All goals completed! 🐙

The Collatz conjecture then states that the sequence beginning from any positive number reaches 1:

def Collatz := ∀ n : Nat, n ≠ 0 → CollatzHoldsFor n

If you succeed in proving this conjecture, you've got a bright future as a number theorist! But don't spend too long on it — it's been open since 1937.

Note to developers (Chris Henson @chenson2018)

We may want to add an exercise later proving false if one assumes Collatz' conjecture without the n ≠ 0 assumption. We had that mistake in the script for years and no one noticed, wow!

theorem Collatz0' {n : Nat} (h₀ : n = 0) : ¬ CollatzHoldsFor n := n:Nath₀:n = 0⊢ ¬CollatzHoldsFor n n:Nath₀:n = 0h:CollatzHoldsFor n⊢ False induction h with n:Nath₀:1 = 0⊢ False All goals completed! 🐙 n:Natn✝:Nath₁✝:n✝.even = trueh₂✝:CollatzHoldsFor (div2 n✝)ih:div2 n✝ = 0 → Falseh₀:n✝ = 0⊢ False n:Natn✝:Nath₁✝:n✝.even = trueh₂✝:CollatzHoldsFor (div2 n✝)ih:div2 n✝ = 0 → Falseh₀:n✝ = 0⊢ div2 n✝ = 0; n:Natn✝:Nath₁✝:n✝.even = trueh₂✝:CollatzHoldsFor (div2 n✝)ih:div2 n✝ = 0 → Falseh₀:n✝ = 0⊢ div2 0 = 0; All goals completed! 🐙 n:Natn✝:Nath:n✝.even = falseh₂✝:CollatzHoldsFor (3 * n✝ + 1)h₂_ih✝:3 * n✝ + 1 = 0 → Falseh₀:n✝ = 0⊢ False n:Natn✝:Nath:Nat.even 0 = falseh₂✝:CollatzHoldsFor (3 * n✝ + 1)h₂_ih✝:3 * n✝ + 1 = 0 → Falseh₀:n✝ = 0⊢ False; n:Natn✝:Nath:true = falseh₂✝:CollatzHoldsFor (3 * n✝ + 1)h₂_ih✝:3 * n✝ + 1 = 0 → Falseh₀:n✝ = 0⊢ False; All goals completed! 🐙 theorem Collatz0 : ¬ (∀ n, CollatzHoldsFor n) := ⊢ ¬∀ (n : Nat), CollatzHoldsFor n h:∀ (n : Nat), CollatzHoldsFor n⊢ False; h:∀ (n : Nat), CollatzHoldsFor n⊢ ?n = 0h:∀ (n : Nat), CollatzHoldsFor n⊢ CollatzHoldsFor ?nh:∀ (n : Nat), CollatzHoldsFor n⊢ Nat; h:∀ (n : Nat), CollatzHoldsFor n⊢ CollatzHoldsFor 0; All goals completed! 🐙

9.1.2. Example: Binary Relation for Comparing Numbers🔗

A binary relation on a set α has Lean type α → α → Prop. This is a family of propositions parameterized by two elements of α — i.e., a proposition about pairs of elements of α.

For example, one familiar binary relation on Nat is Le : Nat → Nat → Prop, the less-than-or-equal-to relation, which can be inductively defined by the following two rules:

  ─────── (le_refl)
  Le n n

  Le n m
──────────── (le_step)
Le n (m + 1)

These rules say that there are two ways to show that a number is less than or equal to another: either observe that they are the same number, or, if the second has the form m + 1, give evidence that the first is less than or equal to m.

namespace LePlayground inductive Le : Nat → Nat → Prop where | refl {n : Nat} : Le n n | step {n m : Nat} (h : Le n m) : Le n (m + 1) scoped infix:50 (priority := high) " ≤ " => Le

This definition is a bit simpler and more elegant than the boolean function Nat.ble we defined in Basics. As usual, Le and Nat.ble are equivalent, and there is an exercise about that later.

example : 3 ≤ 5 := ⊢ 3 ≤ 5 ⊢ 3 ≤ 4; ⊢ 3 ≤ 3; All goals completed! 🐙 end LePlayground

9.1.3. Example: Transitive Closure🔗

Another example: the transitive closure of a relation r is the smallest relation that contains r and that is transitive. This can be defined by the following two rules:

              r x y
         ─────────────── (t_step)
         TransGen r x y

TransGen r x y    TransGen r y z
──────────────────────────────────── (t_trans)
         TransGen r x z

In Lean this looks as follows:

inductive TransGen {α : Type} (r : α → α → Prop) : α → α → Prop where | step {x y : α} (h : r x y) : TransGen r x y | trans {x y z : α} (h₁ : TransGen r x y) (h₂ : TransGen r y z) : TransGen r x z

"Gen" is short for "generated by" — TransGen r means the smallest transitive relation generated by r.

For example, suppose we define a "parent of" relation on a group of people...

inductive Person : Type where | sage | cleo | ridley | moss inductive ParentOf : Person → Person → Prop where | sage_cleo : ParentOf .sage .cleo | sage_ridley : ParentOf .sage .ridley | cleo_moss : ParentOf .cleo .moss

In this example, sage is a parent of both cleo and ridley; and cleo is a parent of moss.

The ParentOf relation is not transitive, but we can define an "ancestor of" relation as its transitive closure:

def AncestorOf : Person → Person → Prop := TransGen ParentOf

Here is a derivation showing that sage is an ancestor of moss:

 ——————————————————— (sage_cleo) ——————————————————— (cleo_moss)
 ParentOf .sage .cleo            ParentOf .cleo .moss
————————————————————— (step)    ————————————————————— (step)
AncestorOf .sage .cleo          AncestorOf .cleo .moss
———————————————————————————————————————————————————— (trans)
                AncestorOf .sage .moss
example : AncestorOf .sage .moss := ⊢ AncestorOf sage moss ⊢ TransGen ParentOf sage ?y⊢ TransGen ParentOf ?y moss⊢ Person ⊢ TransGen ParentOf sage ?y ⊢ ParentOf sage ?y✝; All goals completed! 🐙 ⊢ TransGen ParentOf cleo moss ⊢ ParentOf cleo moss; All goals completed! 🐙
Note to developers (Chris Henson)

A simple exercise could be nice here?

Computing the transitive closure can be undecidable even for a relation r that is decidable (e.g., the CollatzStep relation below, whose closure is CollatzStepMulti), so in general we can't expect to define transitive closure as a boolean function. Fortunately, Lean allows us to define transitive closure as an inductive relation.

The transitive closure of a binary relation cannot, in general, be expressed in first-order logic (see the Logic chapter), since doing so would require quantifying over relations themselves. The logic of Lean is, however, much more powerful — being higher-order, as we saw there — and can easily define such inductive relations.

9.1.4. Example: Reflexive and Transitive Closure🔗

As another example, the reflexive and transitive closure of a relation r is the smallest relation that contains r and that is reflexive and transitive. This can be defined by the following three rules (where we added a reflexivity rule to TransGen):

                   r x y
         ——————————————————————— (step)
           ReflTransGen r x y

         ——————————————————————— (refl)
           ReflTransGen r x x

   ReflTransGen r x y    ReflTransGen r y z
—————————————————————————————————————————————— (trans)
           ReflTransGen r x z
inductive ReflTransGen {α : Type} (r : α → α → Prop) : α → α → Prop where | step {x y : α} (h : r x y) : ReflTransGen r x y | refl {x : α} : ReflTransGen r x x | trans {x y z : α} (h₁ : ReflTransGen r x y) (h₂ : ReflTransGen r y z) : ReflTransGen r x z

For instance, this enables an equivalent definition of the Collatz conjecture. First we define a binary relation corresponding to the "Collatz step function" collatzStep:

def CollatzStep (n m : Nat) : Prop := collatzStep n = m

This Collatz step relation can be used in conjunction with the reflexive and transitive closure operation to define a Collatz multi-step relation, expressing that a number n reaches another number m in zero or more Collatz steps:

def CollatzStepMulti (n m : Nat) : Prop := ReflTransGen CollatzStep n m def Collatz' : Prop := ∀ (n : Nat), n ≠ 0 → CollatzStepMulti n 1

This CollatzStepMulti relation defined in terms of ReflTransGen allows for more interesting derivations than the linear ones of the directly defined CollatzHoldsFor relation:

collatzStep 16 = 8          collatzStep 8 = 4          collatzStep 4 = 2          collatzStep 2 = 1
────────────────── (step)   ───────────────── (step)   ───────────────── (step)   ───────────────── (step)
CollatzStepMulti 16 8       CollatzStepMulti 8 4       CollatzStepMulti 4 2       CollatzStepMulti 2 1
──────────────────────────────────────────── (trans)   ─────────────────────────────────────────── (trans)
              CollatzStepMulti 16 4                                  CollatzStepMulti 4 1
              ───────────────────────────────────────────────────────────────────────── (trans)
                                     CollatzStepMulti 16 1
Exercise★(EqvGen) (Optional, Manually graded)

How would you modify the ReflTransGen definition above to define the reflexive, symmetric, and transitive closure of r?

N.B. The reflexive, symmetric, and transitive closure of a relation is also called its equivalence closure.

inductive EqvGen {α : Type} (r : α → α → Prop) : α → α → Prop where | refl {x : α} : EqvGen r x x | step {x y : α} (h : r x y) : EqvGen r x y | sym {x y : α} (h : EqvGen r y x) : EqvGen r x y | trans {x y z : α} (h₁ : EqvGen r x y) (h₂ : EqvGen r y z) : EqvGen r x z

9.1.5. Example: Permutations🔗

The familiar mathematical concept of permutation also has an elegant formulation as an inductive relation. For simplicity, let's focus on permutations of lists with exactly three elements.

We can define such permutations by the following rules:

   ───────────────────────── (swap12)
   Perm3 [a, b, c] [b, a, c]

   ───────────────────────── (swap23)
   Perm3 [a, b, c] [a, c, b]

Perm3 l₁ l₂       Perm3 l₂ l₃
───────────────────────────── (trans)
         Perm3 l₁ l₃

For instance, we can derive Perm3 [1, 2, 3] [3, 2, 1] as follows:

───────────────────────── (swap12)  ─────────────────────── (swap23)
Perm3 [1, 2, 3] [2, 1, 3]            Perm3 [2, 1, 3] [2, 3, 1]
─────────────────────────────────────────────────────────────────(trans)    ───────────────────── (swap12)
Perm3 [1, 2, 3] [2, 3, 1]                                                    Perm3 [2, 3, 1] [3, 2, 1]
───────────────────────────────────────────────────────────────────────────────────────────────────────── (trans)
Perm3 [1, 2, 3] [3, 2, 1]

This definition says:

  • If l₂ can be obtained from l₁ by swapping the first and second elements, then l₂ is a permutation of l₁.

  • If l₂ can be obtained from l₁ by swapping the second and third elements, then l₂ is a permutation of l₁.

  • If l₂ is a permutation of l₁ and l₃ is a permutation of l₂, then l₃ is a permutation of l₁.

In Lean, we can define Perm3 as follows:

inductive Perm3 {α : Type} : List α → List α → Prop where | swap12 {x y z : α} : Perm3 [x, y, z] [y, x, z] | swap23 {x y z : α} : Perm3 [x, y, z] [x, z, y] | trans {l₁ l₂ l₃ : List α} (h₁₂ : Perm3 l₁ l₂) (h₂₃ : Perm3 l₂ l₃) : Perm3 l₁ l₃
Exercise★(perm) (Optional, Manually graded)

According to this definition, is [1, 2, 3] a permutation of itself?

Yes! Just apply Perm3.swap12 twice (or Perm3.swap23 twice).

9.1.6. Example: Evenness (yet again)🔗

We've already seen two ways of stating a proposition that a number n is even: We can say

(1) Nat.even n = true (using the recursive boolean function Nat.even), or

(2) ∃ k, n = Nat.double k (using an existential quantifier).

A third possibility, which we'll use as a simple running example in this chapter, is to say that a number is even if we can establish its evenness from the following two rules:

  ────────── (zero)
    Even 0

    Even n
—————————————— (succ_succ)
  Even (n + 2)

Intuitively these rules say that:

  • The number 0 is even.

  • If n is even, then n + 2 is even.

(Defining evenness in this way may seem a bit confusing, since we have already seen two perfectly good ways of doing it. It makes a convenient running example because it is simple and compact, but we will soon return to the more compelling examples above.)

To illustrate how this new definition of evenness works, let's imagine using it to show that 4 is even:

                 ──────── (zero)
                  Even 0
          ─────────────────────── (succ_succ)
          Even (.succ (.succ 0))
────────────────────────────────────────────── (succ_succ)
Even (.succ (.succ (.succ (.succ 0))))

In words, to show that 4 is even, by rule succ_succ, it suffices to show that 2 is even. This, in turn, is again guaranteed by rule succ_succ, as long as we can show that 0 is even. But this last fact follows directly from the zero rule.

We can translate the informal definition of evenness from above into a formal inductive declaration, where each "way that a number can be even" corresponds to a separate constructor:

inductive Even : Nat → Prop where | zero : Even 0 | succ_succ {n : Nat} (h : Even n) : Even (n + 2)

Such definitions are interestingly different from previous uses of inductive for defining inductive datatypes like Nat or List. For one thing, we are defining not a Type (like Nat) or a function yielding a Type (like List), but rather a function from Nat to Prop — that is, a property of numbers. But what is really new is that, because the Nat argument of Even appears to the right of the colon on the first line, it is allowed to take different values in the types of different constructors: 0 in the type of Even.zero and (n + 2) in the type of Even.succ_succ. Accordingly, the type of each constructor must be specified explicitly (after a colon), and each constructor's type must have the form Even n for some natural number n.

In contrast, recall the definition of List:

inductive `List` has already been declaredList (α : Type) : Type where | nil | cons (x : α) (l : List α)

or (equivalently but more explicitly):

inductive `List` has already been declaredList (α : Type) : Type where | nil : List α | cons (x : α) (l : List α) : List α

This definition introduces the α parameter globally, to the left of the colon, forcing the result of List.nil and List.cons to be the same type (i.e., List α). But if we had tried to bring Nat to the left of the colon in defining Even, we would have seen an error:

inductive WrongEven (n : Nat) : Prop where Mismatched inductive type parameter in WrongEven 0 The provided argument 0 is not definitionally equal to the expected parameter n Note: The value of parameter `n` must be fixed throughout the inductive declaration. Consider making this parameter an index if it must vary.| zero : WrongEven 0 | succ_succ (h : WrongEven n) : WrongEven (.succ (.succ n))
Mismatched inductive type parameter in
  WrongEven 0
The provided argument
  0
is not definitionally equal to the expected parameter
  n

Note: The value of parameter `n` must be fixed throughout the inductive declaration. Consider making this parameter an index if it must vary.

In an inductive definition, an argument to the type constructor on the left of the colon is called a "parameter," whereas an argument on the right is called an "index" or "annotation."

For example, in inductive List (α : Type) ..., the α is a parameter, while in inductive Even : Nat → Prop ..., the unnamed Nat argument is an index.

We can think of the inductive definition of Even as defining a Lean property Even : Nat → Prop, together with two "evidence constructors":

Even : Nat → Prop#check (Even) Even.zero : Even 0#check Even.zero Even.succ_succ {n : Nat} (h : Even n) : Even (n + 2)#check Even.succ_succ
Even : Nat → Prop
Even.zero : Even 0
Even.succ_succ {n : Nat} (h : Even n) : Even (n + 2)

These evidence constructors can be thought of as "primitive evidence of evenness," and they can be used later on just like proven theorems. In particular, we can use Lean's apply and exact tactics with the constructor names to obtain evidence for Even of particular numbers...

namespace Even example : Even 4 := ⊢ Even 4 ⊢ Even 2; ⊢ Even 0; All goals completed! 🐙

... or we can use function application syntax to combine several constructors:

example : Even 4 := ⊢ Even 4 All goals completed! 🐙

... or we can also use the constructor tactic we saw earlier to select the appropriate inductive constructor:

example : Even 4 := ⊢ Even 4 ⊢ Even 2; ⊢ Even 0; All goals completed! 🐙

In this way, we can also prove theorems that have hypotheses involving Even.

theorem plus4 (n : Nat) (h : Even n) : Even (4 + n) := n:Nath:Even n⊢ Even (4 + n) n:Nath:Even n⊢ Even (n + 4) All goals completed! 🐙
Exercise★(double)
theorem double (n : Nat) : Even n.double := n:Nat⊢ Even n.double solution! induction n with ⊢ Even (Nat.double 0) ⊢ Even 0; All goals completed! 🐙 n:Natih:Even n.double⊢ Even (n + 1).double n:Natih:Even n.double⊢ Even (n.double + 2); All goals completed! 🐙
end Even

9.1.7. Constructing Evidence for Permutations🔗

Similarly, we can apply the evidence constructors to obtain evidence of Perm3 [1, 2, 3] [3, 2, 1]:

namespace Perm3 theorem rev : Perm3 [1, 2, 3] [3, 2, 1] := ⊢ Perm3 [1, 2, 3] [3, 2, 1] ⊢ Perm3 [1, 2, 3] [2, 3, 1]⊢ Perm3 [2, 3, 1] [3, 2, 1] ⊢ Perm3 [1, 2, 3] [2, 3, 1] ⊢ Perm3 [1, 2, 3] [2, 1, 3]⊢ Perm3 [2, 1, 3] [2, 3, 1] ⊢ Perm3 [1, 2, 3] [2, 1, 3] All goals completed! 🐙 ⊢ Perm3 [2, 1, 3] [2, 3, 1] All goals completed! 🐙 ⊢ Perm3 [2, 3, 1] [3, 2, 1] All goals completed! 🐙

And again we can equivalently use function application syntax to combine several constructors. (Note that the Lean type checker can infer not only types, but also Nats and Lists, when they are clear from the context.)

theorem rev' : Perm3 [1, 2, 3] [3, 2, 1] := ⊢ Perm3 [1, 2, 3] [3, 2, 1] All goals completed! 🐙

So the informal derivation trees we drew above are not too far from what's happening formally. Formally, we're using the evidence constructors to build evidence trees, similar to the finite trees we built using the constructors of data types such as Nat, List, binary trees, etc.

Exercise★(Perm3)
theorem ex1 : Perm3 [1, 2, 3] [2, 3, 1] := ⊢ Perm3 [1, 2, 3] [2, 3, 1] solution! ⊢ Perm3 [1, 2, 3] [2, 1, 3]⊢ Perm3 [2, 1, 3] [2, 3, 1] ⊢ Perm3 [1, 2, 3] [2, 1, 3] All goals completed! 🐙 ⊢ Perm3 [2, 1, 3] [2, 3, 1] All goals completed! 🐙 theorem refl (α : Type) (a b c : α) : Perm3 [a, b, c] [a, b, c] := α:Typea:αb:αc:α⊢ Perm3 [a, b, c] [a, b, c] solution! α:Typea:αb:αc:α⊢ Perm3 [a, b, c] [b, a, c]α:Typea:αb:αc:α⊢ Perm3 [b, a, c] [a, b, c] α:Typea:αb:αc:α⊢ Perm3 [a, b, c] [b, a, c] All goals completed! 🐙 α:Typea:αb:αc:α⊢ Perm3 [b, a, c] [a, b, c] All goals completed! 🐙
end Perm3

9.2. Using Evidence in Proofs🔗

Besides constructing evidence that numbers are even, we can also destruct such evidence, reasoning about how it could have been built — i.e., we can introduce and eliminate Even evidence, in the sense of Logic.

Defining Even with an inductive declaration tells Lean not only that the constructors Even.zero and Even.succ_succ are valid ways to build evidence that some number is Even, but also that these two constructors are the only ways to build evidence that numbers are Even.

In other words, if someone gives us evidence e for the proposition Even n, then we know that e must be one of two things:

  • e = Even.zero and n = 0, or

  • e = Even.succ_succ n' e' and n = n' + 2, where e' is evidence for Even n'.

This suggests that it should be possible to analyze a hypothesis of the form Even n much as we do inductively defined data structures; in particular, it should be possible to argue either by case analysis or by induction on such evidence. Let's look at a few examples to see what this means in practice.

9.2.1. Destructing and Inverting Evidence🔗

Suppose we are proving some fact involving a number n, and we are given Even n as a hypothesis. We already know how to perform case analysis on n using cases or induction, generating separate subgoals for the case where n = 0 and the case where n = n' + 1 for some n'. But for some proofs we may instead want to analyze the evidence for Even n directly.

As a tool for such proofs, we can formalize the intuitive characterization that we gave above for evidence of Even n, using cases.

theorem Even.inversion (n : Nat) (h : Even n) : (n = 0) ∨ ∃ n', n = n' + 2 ∧ Even n' := n:Nath:Even n⊢ n = 0 ∨ ∃ n', n = n' + 2 ∧ Even n' cases h with ⊢ 0 = 0 ∨ ∃ n', 0 = n' + 2 ∧ Even n' ⊢ 0 = 0; All goals completed! 🐙 n:Nath:Even n⊢ n + 2 = 0 ∨ ∃ n', n + 2 = n' + 2 ∧ Even n' n:Nath:Even n⊢ ∃ n', n + 2 = n' + 2 ∧ Even n'; All goals completed! 🐙

Facts like this are often called "inversion lemmas" because they allow us to "invert" some given information to reason about all the different ways it could have been derived.

Exercise★(le_inversion)

Let's prove a similar inversion lemma for Le.

namespace LePlayground theorem le_inversion (n m : Nat) (h : n ≤ m) : (n = m) ∨ (∃ m', m = m' + 1 ∧ n ≤ m') := n:Natm:Nath:n ≤ m⊢ n = m ∨ ∃ m', m = m' + 1 ∧ n ≤ m' solution! cases h with n:Nat⊢ n = n ∨ ∃ m', n = m' + 1 ∧ n ≤ m' n:Nat⊢ n = n; All goals completed! 🐙 n:Natm:Nath:n ≤ m⊢ n = m + 1 ∨ ∃ m', m + 1 = m' + 1 ∧ n ≤ m' n:Natm:Nath:n ≤ m⊢ ∃ m', m + 1 = m' + 1 ∧ n ≤ m'; All goals completed! 🐙
end LePlayground
Quiz

Which tactics are needed to prove this goal?

∀ (n : Nat), Even n → n = 1 → true = false

(A) cases (B) contradiction (C) Both cases and contradiction (D) These tactics are not sufficient to solve the goal.

Show solution
example (n : Nat) (hEven : Even n) (h : n = 1) : true = false := n:NathEven:Even nh:n = 1⊢ true = false cases hEven with h:0 = 1⊢ true = false All goals completed! 🐙 n✝:Nath✝:Even n✝h:n✝ + 2 = 1⊢ true = false n✝:Nath✝:Even n✝n_eq✝:n✝ + 1 = 0⊢ true = false; All goals completed! 🐙

We can use the inversion lemma that we proved above to help structure proofs:

theorem Even.of_succ_succ (n : Nat) (h : Even (n + 2)) : Even n := n:Nath:Even (n + 2)⊢ Even n n:Nath:n + 2 = 0 ∨ ∃ n', n + 2 = n' + 2 ∧ Even n'⊢ Even n n:Natn':Nath₁:n + 2 = n' + 2h₂:Even n'⊢ Even n n:Natn':Nath₂:Even n'heq:n = n'⊢ Even n n:Nath₂:Even n⊢ Even n All goals completed! 🐙

Note how the inversion lemma produces two subgoals, which correspond to the two ways of proving Even. The first subgoal is a contradiction that is discharged with contradiction. The second subgoal makes use of injections and subst. The subst tactic takes an equation x = t and replaces x by t in the context's hypotheses and in the goal, then removes that equation from the context.

We've defined a handy tactic called inversion that factors out this common pattern, saving us the trouble of explicitly stating and proving an inversion lemma for every inductive definition we make. (The details of how inversion is implemented are beyond the scope of this course. Lean provides metaprogramming facilities that its users can employ to write their own tactics, and these capabilities are powerful enough that just about any algorithmic reasoning steps can be implemented.)

Here, the inversion tactic can detect (1) that the first case, where n = 0, does not apply and (2) that the n' that appears in the Even.succ_succ case must be the same as n.

example (n : Nat) (h : Even (n + 2)) : Even n := n:Nath:Even (n + 2)⊢ Even n n:Nath✝:Even n⊢ Even n; All goals completed! 🐙

The inversion tactic can apply the principle of explosion to "obviously contradictory" hypotheses involving inductively defined properties, something that takes a bit more work using our inversion lemma. Compare:

example : ¬ Even 1 := ⊢ ¬Even 1 h:Even 1⊢ False; h:1 = 0 ∨ ∃ n', 1 = n' + 2 ∧ Even n'⊢ False n':Nath₁:1 = n' + 2h₂:Even n'⊢ False All goals completed! 🐙 example : ¬ Even 1 := ⊢ ¬Even 1 h:Even 1⊢ False; All goals completed! 🐙
Exercise★(inversion_practice)

Prove the following result using inversion. (For extra practice, you can also prove it using the inversion lemma.)

theorem Even.of_add_four {n : Nat} (h : Even (n + 4)) : Even n := n:Nath:Even (n + 4)⊢ Even n solution! inversion h with | succ_succ h' => n:Nath':Even (n + 2)⊢ Even (n + 2); All goals completed! 🐙
Exercise★(even5_nonsense)

Prove the following result using inversion.

theorem Even.even5_nonsense (h : Even 5) : 2 + 2 = 9 := h:Even 5⊢ 2 + 2 = 9 solution! inversion h with | succ_succ h' => inversion h' with | succ_succ h'' => /- Contradiction, as neither constructor can possibly apply... -/ All goals completed! 🐙

Recall from the Logic chapter that equality (Eq) is itself an inductively defined proposition, so inversion can also be used on equality propositions.

We can use inversion to re-prove some theorems from Tactics.

example (n m o : Nat) (h : [n, m] = [o, o]) : [n] = [m] := n:Natm:Nato:Nath:[n, m] = [o, o]⊢ [n] = [m] n:Nat⊢ [n] = [n]; All goals completed! 🐙 example (n : Nat) (h : n + 1 = 0) : 2 + 2 = 5 := n:Nath:n + 1 = 0⊢ 2 + 2 = 5 All goals completed! 🐙

For the inductively defined propositions we use, inversion behaves much like cases: it performs case analysis on the constructors of the hypothesis's inductive type. However, when the case analysis on an indexed proposition gives unsolvable equations between its indices, cases itself fails, whereas inversion leaves such equations in the context.

For example, cases would immediately fail on h:

example (n : Nat) (h : Even (n * n)) : n * n = 0 ∨ ∃ m, n * n = m + 2 := n:Nath:Even (n * n)⊢ n * n = 0 ∨ ∃ m, n * n = m + 2 All goals completed! 🐙
Dependent elimination failed: Failed to solve equation
  n.mul n = 0

inversion instead leaves the equations in the context, where we can use them directly:

example (n : Nat) (h : Even (n * n)) : n * n = 0 ∨ ∃ m, n * n = m + 2 := n:Nath:Even (n * n)⊢ n * n = 0 ∨ ∃ m, n * n = m + 2 inversion h with | zero => n:Nath:Even (n * n)h✝¹:h ≍ Even.zeroh✝:n.mul n = 0⊢ n * n = 0; All goals completed! 🐙 | succ_succ m' _ _ _ => n:Nath:Even (n * n)m':Nath✝²:Even m'h✝¹:h ≍ ⋯h✝:n.mul n = (m'.add 1).succ⊢ ∃ m, n * n = m + 2; All goals completed! 🐙

Here is a useful way to think about inversion. For an inductively defined hypothesis h, inversion h starts with one case for each constructor, then uses the indices of the type of h to eliminate impossible cases, and simplifies the remaining ones. In the remaining cases, it solves these equations to force some expressions or substitute some variables. If an equation cannot be solved, inversion leaves it in the context and we can use it in the rest of the proof.

Quiz

Which tactics are needed to prove this goal, in addition to apply or exact?

∀ n, Even (2 + n) → Even n

(A) inversion (B) inversion, injections (C) inversion, rw [Nat.add_comm] (D) inversion, rw [Nat.add_comm], injections

Show solution
example (n : Nat) (h : Even (2 + n)) : Even n := n:Nath:Even (2 + n)⊢ Even n n:Nath:Even (n + 2)⊢ Even n inversion h with | _ h => All goals completed! 🐙

9.2.2. Induction on Evidence🔗

The Even.double exercise above allows us to easily show that our new notion of evenness is implied by the two earlier ones. In fact, by Nat.even_bool_prop in the Logic chapter, we already know that those are equivalent to each other. To show that Nat.Even, Even, and Nat.even coincide, we just need the following lemma.

We could try to proceed by cases or induction on n. But since Even is mentioned in a premise, this strategy seems unpromising, because (as we've noted before) the induction hypothesis will talk about n - 1 (which is not even!). Thus, it seems better to first try inversion on the evidence for Even.

example (n : Nat) (h : Even n) : Nat.Even n := n:Nath:Even n⊢ n.Even unsolved goals n':Nath':Even n'⊢ (n' + 2).Eveninversion h with | zero => All goals completed! 🐙 -- The first case can be solved trivially. | succ_succ n' h' => unexpected end of input; expected '{'

Unfortunately, the second case is harder. We need to show ∃ n₀, n' + 2 = double n₀, but the only available assumption is h', which states that Even n' holds. In other words, what we need here is precisely the result we are trying to prove, but applied to the smaller evidence h'.

If this story feels familiar, it is no coincidence: we encountered similar problems in the Induction chapter, when trying to use case analysis to prove results that required induction. And once again the solution is... induction!

The behavior of induction on evidence is the same as its behavior on data: it causes Lean to generate one subgoal for each constructor that could have been used to build that evidence, while providing an induction hypothesis for each recursive occurrence of the property in question.

To prove that a property of n holds for all even numbers (i.e., those for which Even n holds), we can use induction on Even n. This requires us to prove two things, corresponding to the two ways in which Even n could have been constructed. If it was constructed by Even.zero, then n = 0 and the property must hold of 0. If it was constructed by Even.succ_succ, then the evidence of Even n is of the form Even.succ_succ n' h', where n = n' + 2 and h' is evidence for Even n'. In this case, the induction hypothesis says that the property we are trying to prove holds for n'.

Let's try proving that lemma again:

theorem Even.nat_even (n : Nat) (h : Even n) : Nat.Even n := n:Nath:Even n⊢ n.Even induction h with n:Nat⊢ Nat.Even 0 All goals completed! 🐙 -- (`0 = double 0` is closed by `exists`'s final `rfl`) n:Natn✝:Nath':Even n✝ih:n✝.Even⊢ (n✝ + 2).Even n:Natn✝:Nath':Even n✝ih:n✝.Evenk:Nathk:n✝ = k.double⊢ (n✝ + 2).Even n:Natn✝:Nath':Even n✝ih:n✝.Evenk:Nathk:n✝ = k.double⊢ n✝ + 2 = (k + 1).double; All goals completed! 🐙

Here, we can see that Lean produced an ih that corresponds to h, the single recursive occurrence of Even in its own definition. Since h' mentions n', the induction hypothesis talks about n', as opposed to n or some other number.

The equivalence between the second and third definitions of evenness now follows.

theorem Even.iff_nat_even (n : Nat) : Even n ↔ Nat.Even n := n:Nat⊢ Even n ↔ n.Even n:Nat⊢ Even n → n.Evenn:Nat⊢ n.Even → Even n n:Nat⊢ Even n → n.Even n:Nath:Even n⊢ n.Even; All goals completed! 🐙 n:Nat⊢ n.Even → Even n n:Natk:Nathk:n = k.double⊢ Even n; n:Natk:Nathk:n = k.double⊢ Even k.double; All goals completed! 🐙

As we will see in later chapters, induction on evidence is a recurring technique across many areas — in particular for formalizing the semantics of programming languages.

The following exercises provide simpler examples of this technique, to help you familiarize yourself with it.

Exercise★★(Even.add)
theorem Even.add (n m : Nat) (hn : Even n) (hm : Even m) : Even (n + m) := n:Natm:Nathn:Even nhm:Even m⊢ Even (n + m) solution! induction hn with n:Natm:Nathm:Even m⊢ Even (0 + m) n:Natm:Nathm:Even m⊢ Even m; All goals completed! 🐙 n:Natm:Nathm:Even mn✝:Nath':Even n✝ih:Even (n✝ + m)⊢ Even (n✝ + 2 + m) n:Natm:Nathm:Even mn✝:Nath':Even n✝ih:Even (n✝ + m)⊢ Even (n✝ + m).succ.succ n:Natm:Nathm:Even mn✝:Nath':Even n✝ih:Even (n✝ + m)⊢ Even (n✝ + m); All goals completed! 🐙
Exercise★★★(Even.of_add_left) (Advanced)
theorem Even.of_add_left (n m : Nat) (h : Even (n + m)) (hn : Even n) : Even m := n:Natm:Nath:Even (n + m)hn:Even n⊢ Even m /- Hint: There are two pieces of evidence you could attempt to induct upon here. If one doesn't work, try the other. -/ solution! induction hn generalizing m with n:Natm:Nath:Even (0 + m)⊢ Even m n:Natm:Nath:Even m⊢ Even m; All goals completed! 🐙 n:Natn✝:Nath':Even n✝ih:∀ (m : Nat), Even (n✝ + m) → Even mm:Nath:Even (n✝ + 2 + m)⊢ Even m n:Natn✝:Nath':Even n✝ih:∀ (m : Nat), Even (n✝ + m) → Even mm:Nath:Even (n✝ + m).succ.succ⊢ Even m n:Natn✝:Nath':Even n✝ih:∀ (m : Nat), Even (n✝ + m) → Even mm:Nath✝:Even (n✝ + m)⊢ Even m; n:Natn✝:Nath':Even n✝ih:∀ (m : Nat), Even (n✝ + m) → Even mm:Nath✝:Even (n✝ + m)⊢ Even (n✝ + m); All goals completed! 🐙
Exercise★★★(add_of_add_left) (Optional)

This exercise can be completed without induction or case analysis. But you will need a clever have and some tedious rewriting. Hint: Is (n + m) + (n + k) even?

theorem Even.add_of_add_left (n m k : Nat) (hₙₘ : Even (n + m)) (hₙₖ : Even (n + k)) : Even (m + k) := n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)⊢ Even (m + k) solution! n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)⊢ Even (n + n + (m + k))n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)⊢ Even (n + n) n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)⊢ Even (n + n + (m + k)) n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)h:n + n + (m + k) = n + m + (n + k)⊢ Even (n + n + (m + k)) n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)h:n + n + (m + k) = n + m + (n + k)⊢ Even (n + m + (n + k)) n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)h:n + n + (m + k) = n + m + (n + k)⊢ Even (n + m)n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)h:n + n + (m + k) = n + m + (n + k)⊢ Even (n + k) n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)h:n + n + (m + k) = n + m + (n + k)⊢ Even (n + m) All goals completed! 🐙 n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)h:n + n + (m + k) = n + m + (n + k)⊢ Even (n + k) All goals completed! 🐙 n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)⊢ Even (n + n) n:Natm:Natk:Nathₙₘ:Even (n + m)hₙₖ:Even (n + k)⊢ Even n.double; All goals completed! 🐙

Another example of a proposition that can be characterized both recursively and inductively is the List.In predicate we defined in the Logic chapter. As a reminder, the recursive definition we saw looked like this:

def List.In {α : Type} (x : α) (xs : List α) : Prop := match xs with | [] => False | x' :: xs' => x = x' ∨ In x xs'

We can also write this definition inductively like so:

inductive List.In' {α : Type} (x : α) : List α → Prop | head {l : List α} : In' x (x :: l) | tail {y : α} {l : List α} (h : In' x l) : In' x (y :: l)

In fact, this is exactly how Lean defines this proposition, which it calls Membership.mem and which is written x ∈ l. Its negation ¬ x ∈ l is also written as x ∉ l.

A good exercise to test your understanding of induction on evidence is to prove the equivalence of these definitions:

Exercise★★(in_mem)
theorem List.in_iff_mem {α} (x : α) (l : List α) : List.In x l ↔ x ∈ l := α:Typex:αl:List α⊢ In x l ↔ x ∈ l solution! α:Typex:αl:List α⊢ In x l → x ∈ lα:Typex:αl:List α⊢ x ∈ l → In x l α:Typex:αl:List α⊢ In x l → x ∈ l α:Typex:αl:List αh:In x l⊢ x ∈ l; induction l with α:Typex:αh:In x []⊢ x ∈ [] α:Typex:αh:False⊢ x ∈ []; All goals completed! 🐙 α:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:In x✝ (x :: xs)⊢ x✝ ∈ x :: xs α:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:x✝ = x ∨ In x✝ xs⊢ x✝ ∈ x :: xs α:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:x✝ = x⊢ x✝ ∈ x :: xsα:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:In x✝ xs⊢ x✝ ∈ x :: xs α:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:x✝ = x⊢ x✝ ∈ x :: xs α:Typex:αxs:List αih:In x✝ xs → x✝ ∈ xs⊢ x ∈ x :: xs; All goals completed! 🐙 α:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:In x✝ xs⊢ x✝ ∈ x :: xs α:Typex✝:αx:αxs:List αih:In x✝ xs → x✝ ∈ xsh:In x✝ xs⊢ Mem x✝ xs; All goals completed! 🐙 α:Typex:αl:List α⊢ x ∈ l → In x l α:Typex:αl:List αh:x ∈ l⊢ In x l; induction h with α:Typex:αl:List αl':List α⊢ In x (x :: l') α:Typex:αl:List αl':List α⊢ x = x ∨ In x l'; α:Typex:αl:List αl':List α⊢ x = x; All goals completed! 🐙 α:Typex:αl:List αh:αas✝:List αih:Mem x as✝a_ih✝:In x as✝⊢ In x (h :: as✝) α:Typex:αl:List αh:αas✝:List αih:Mem x as✝a_ih✝:In x as✝⊢ x = h ∨ In x as✝; α:Typex:αl:List αh:αas✝:List αih:Mem x as✝a_ih✝:In x as✝⊢ In x as✝; All goals completed! 🐙

The characterizing lemmas for ∈ are called List.mem_nil_iff and List.mem_cons.

9.2.3. Multiple Induction Hypotheses🔗

Recall the definition of the reflexive, transitive closure of a relation:

inductive ReflTransGen {α : Type} (r : α → α → Prop) : α → α → Prop where | step {x y : α} (h : r x y) : ReflTransGen r x y | refl {x : α} : ReflTransGen r x x | trans {x y z : α} (h₁ : ReflTransGen r x y) (h₂ : ReflTransGen r y z) : ReflTransGen r x z

Let's say that a relation on a type α is diagonal if it refines the identity relation — i.e., if r x y implies x = y.

Note to developers

NDS 25: I originally wanted to do this with the empty relation, defined inductively, but this requires introducing the surprising behavior of uninhabited types, which I don't think have been covered (yet?). Maybe they should be? BCP 25: This one seems good.

def Diagonal {α : Type} (r : α → α → Prop) := ∀ {x y}, r x y → x = y

Now consider the following lemma about diagonal relations:

theorem closure_of_diagonal_is_diagonal {α : Type} (r : α → α → Prop) (hDiag : Diagonal r) : Diagonal (ReflTransGen r) := α:Typer:α → α → ProphDiag:Diagonal r⊢ Diagonal (ReflTransGen r) α:Typer:α → α → ProphDiag:Diagonal rx:αy:αh:ReflTransGen r x y⊢ x = y induction h with α:Typer:α → α → ProphDiag:Diagonal rx:αy:αx✝:αy✝:αhr:r x✝ y✝⊢ x✝ = y✝ All goals completed! 🐙 α:Typer:α → α → ProphDiag:Diagonal rx:αy:αx✝:α⊢ x✝ = x✝ All goals completed! 🐙 α:Typer:α → α → ProphDiag:Diagonal rx:αy:αx✝:αy✝:αz✝:αh₁✝:ReflTransGen r x✝ y✝h₂✝:ReflTransGen r y✝ z✝ihxy:x✝ = y✝ihyz:y✝ = z✝⊢ x✝ = z✝ All goals completed! 🐙

Something interesting happens here: there are two induction hypotheses, ihxy and ihyz! If you think about it, it is not that weird: we are in the case trans, which has two recursive components, hxy, relating x to y, and hyz, relating y to z. Hence we may want (and will actually need) an induction hypothesis for hxy and one for hyz — they are called ihxy and ihyz here. In general, Lean will always generate one induction hypothesis per recursive premise of each constructor of the type being inducted over.

Note to developers

HIDE: NDS comparing the previous proof to the pen-and-paper version could be an idea to consider, as the way people tend to write it on paper differs a bit from the mechanized proof. BCP 25: Yes.

Exercise★★★★(Even') (Advanced, Optional)

In general, there may be multiple ways of defining a property inductively. For example, here's a (slightly contrived) alternative definition for Even:

inductive Even' : Nat → Prop where | zero : Even' 0 | two : Even' 2 | add {n m : Nat} (h₁ : Even' n) (h₂ : Even' m) : Even' (n + m)

Prove that this definition is logically equivalent to the old one. To streamline the proof, use the technique (from the Logic chapter) of applying theorems to arguments, and note that the same technique works with constructors of inductively defined propositions.

theorem Even'.iff_Even n : Even' n ↔ Even n := n:Nat⊢ Even' n ↔ Even n solution! n:Nat⊢ Even' n → Even nn:Nat⊢ Even n → Even' n n:Nat⊢ Even' n → Even n n:Nath:Even' n⊢ Even n; n:Nat⊢ Even 0n:Nat⊢ Even 2n:Natn✝:Natm✝:Nath₁✝:Even' n✝h₂✝:Even' m✝h₁_ih✝:Even n✝h₂_ih✝:Even m✝⊢ Even (n✝ + m✝) n:Nat⊢ Even 0 All goals completed! 🐙 n:Nat⊢ Even 2 n:Nat⊢ Even 0; All goals completed! 🐙 n:Natn✝:Natm✝:Nath₁✝:Even' n✝h₂✝:Even' m✝h₁_ih✝:Even n✝h₂_ih✝:Even m✝⊢ Even (n✝ + m✝) n:Natn✝:Natm✝:Nath₁✝:Even' n✝h₂✝:Even' m✝h₁_ih✝:Even n✝h₂_ih✝:Even m✝⊢ Even n✝n:Natn✝:Natm✝:Nath₁✝:Even' n✝h₂✝:Even' m✝h₁_ih✝:Even n✝h₂_ih✝:Even m✝⊢ Even m✝; n:Natn✝:Natm✝:Nath₁✝:Even' n✝h₂✝:Even' m✝h₁_ih✝:Even n✝h₂_ih✝:Even m✝⊢ Even m✝; All goals completed! 🐙 n:Nat⊢ Even n → Even' n n:Nath:Even n⊢ Even' n; induction h with n:Nat⊢ Even' 0 All goals completed! 🐙 n✝:Natn:Nath✝:Even nh_ih✝:Even' n⊢ Even' (n + 2) n✝:Natn:Nath✝:Even nh_ih✝:Even' n⊢ Even' (n + 0 + 2) n✝:Natn:Nath✝:Even nh_ih✝:Even' n⊢ Even' (n + 0)n✝:Natn:Nath✝:Even nh_ih✝:Even' n⊢ Even' 2; n✝:Natn:Nath✝:Even nh_ih✝:Even' n⊢ Even' 2; All goals completed! 🐙

We can do similar inductive proofs on the Perm3 relation, which we defined earlier as follows:

inductive Perm3 {α : Type} : List α → List α → Prop where | swap12 {x y z : α} : Perm3 [x, y, z] [y, x, z] | swap23 {x y z : α} : Perm3 [x, y, z] [x, z, y] | trans {l₁ l₂ l₃ : List α} (h₁₂ : Perm3 l₁ l₂) (h₂₃ : Perm3 l₂ l₃) : Perm3 l₁ l₃namespace Perm3 theorem symm {α} (l₁ l₂ : List α) (h : Perm3 l₁ l₂) : Perm3 l₂ l₁ := α:Typel₁:List αl₂:List αh:Perm3 l₁ l₂⊢ Perm3 l₂ l₁ induction h with α:Typel₁:List αl₂:List αx✝:αy✝:αz✝:α⊢ Perm3 [y✝, x✝, z✝] [x✝, y✝, z✝] All goals completed! 🐙 α:Typel₁:List αl₂:List αx✝:αy✝:αz✝:α⊢ Perm3 [x✝, z✝, y✝] [x✝, y✝, z✝] All goals completed! 🐙 α:Typel₁:List αl₂:List αl₁✝:List αl₂✝:List αl₃✝:List αh₁₂✝:Perm3 l₁✝ l₂✝h₂₃✝:Perm3 l₂✝ l₃✝ih₁₂:Perm3 l₂✝ l₁✝ih₂₃:Perm3 l₃✝ l₂✝⊢ Perm3 l₃✝ l₁✝ All goals completed! 🐙

We pause for a moment to point out that some tactics that accept an at clause can target several locations at once, including the goal, written using the ⊢ symbol, by listing them together after at — for instance, both rw and dsimp support this.

example (n m : Nat) (h : n + 0 = m) : n = m + 0 := n:Natm:Nath:n + 0 = m⊢ n = m + 0 n:Natm:Nath:n = m⊢ n = m All goals completed! 🐙

Instead of listing specific targets, you can also write at * to target all the hypotheses and the goal. Here is another example, relevant to the next exercise.

example (hIn : x ∈ [1, 2, 3]) : x ∈ [2, 1, 3] := x:NathIn:x ∈ [1, 2, 3]⊢ x ∈ [2, 1, 3] x:NathIn:x = 1 ∨ x = 2 ∨ x ∈ [3]⊢ x = 2 ∨ x = 1 ∨ x ∈ [3] All goals completed! 🐙
Exercise★★(Perm3_In)

If you find yourself dealing with deeply nested cases in this proof, think back to Logic where you learned about the obtain tactic.

theorem In {α} (x : α) (l₁ l₂ : List α) (hPerm : Perm3 l₁ l₂) (hIn : x ∈ l₁) : x ∈ l₂ := α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:x ∈ l₁⊢ x ∈ l₂ solution! induction hPerm with α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αhIn:x ∈ [x✝, y✝, z✝]⊢ x ∈ [y✝, x✝, z✝] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αhIn:x = x✝ ∨ x = y✝ ∨ x = z✝ ∨ x ∈ []⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ []α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ []α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ []α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x ∈ []⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = x✝ ∨ x = z✝ ∨ x ∈ []; α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = x✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = y✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = x✝ ∨ x = z✝ ∨ x ∈ []; α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = z✝ ∨ x ∈ []; α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = z✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x ∈ []⊢ x = y✝ ∨ x = x✝ ∨ x = z✝ ∨ x ∈ [] All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αhIn:x ∈ [x✝, y✝, z✝]⊢ x ∈ [x✝, z✝, y✝] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αhIn:x = x✝ ∨ x = y✝ ∨ x = z✝ ∨ x ∈ []⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ []α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ []α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ []α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x ∈ []⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = x✝⊢ x = x✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = z✝ ∨ x = y✝ ∨ x ∈ []; α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = y✝ ∨ x ∈ []; α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = y✝⊢ x = y✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ [] α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = z✝ ∨ x = y✝ ∨ x ∈ []; α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x = z✝⊢ x = z✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αx✝:αy✝:αz✝:αh:x ∈ []⊢ x = x✝ ∨ x = z✝ ∨ x = y✝ ∨ x ∈ [] All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αl₁✝:List αl₂✝:List αl₃✝:List αh₁₂✝:Perm3 l₁✝ l₂✝h₂₃✝:Perm3 l₂✝ l₃✝ih₁₂:x ∈ l₁✝ → x ∈ l₂✝ih₂₃:x ∈ l₂✝ → x ∈ l₃✝hIn:x ∈ l₁✝⊢ x ∈ l₃✝ α:Typex:αl₁:List αl₂:List αl₁✝:List αl₂✝:List αl₃✝:List αh₁₂✝:Perm3 l₁✝ l₂✝h₂₃✝:Perm3 l₂✝ l₃✝ih₁₂:x ∈ l₁✝ → x ∈ l₂✝ih₂₃:x ∈ l₂✝ → x ∈ l₃✝hIn:x ∈ l₁✝⊢ x ∈ l₂✝; α:Typex:αl₁:List αl₂:List αl₁✝:List αl₂✝:List αl₃✝:List αh₁₂✝:Perm3 l₁✝ l₂✝h₂₃✝:Perm3 l₂✝ l₃✝ih₁₂:x ∈ l₁✝ → x ∈ l₂✝ih₂₃:x ∈ l₂✝ → x ∈ l₃✝hIn:x ∈ l₁✝⊢ x ∈ l₁✝; All goals completed! 🐙
Exercise★(Perm3_NotIn) (Optional)
theorem NotIn {α} (x : α) (l₁ l₂ : List α) (hPerm : Perm3 l₁ l₂) (hIn : x ∉ l₁) : x ∉ l₂ := α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁⊢ ¬x ∈ l₂ solution! α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ False α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ x ∈ l₁; α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ Perm3 ?l₁ l₁α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ x ∈ ?l₁α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ List α α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ Perm3 ?l₁ l₁ α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ Perm3 l₁ ?l₁✝; All goals completed! 🐙 α:Typex:αl₁:List αl₂:List αhPerm:Perm3 l₁ l₂hIn:¬x ∈ l₁hContra:x ∈ l₂⊢ x ∈ l₂ All goals completed! 🐙
Exercise★★(NotPerm3) (Optional)

Proving that something is not a permutation is quite tricky. Some of the lemmas above, like Perm3.In, can be useful for this.

theorem Not : ¬ Perm3 [1, 2, 3] [1, 2, 4] := ⊢ ¬Perm3 [1, 2, 3] [1, 2, 4] solution! h:Perm3 [1, 2, 3] [1, 2, 4]⊢ False; h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]⊢ False h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ False h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ 3 ∈ [1, 2, 4]; h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ 3 ∈ [1, 2, 3] h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ 3 = 1 ∨ 3 = 2 ∨ 3 = 3 ∨ 3 ∈ [] h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ 3 = 2 ∨ 3 = 3 ∨ 3 ∈ []; h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ 3 = 3 ∨ 3 ∈ []; h✝:Perm3 [1, 2, 3] [1, 2, 4]h:3 ∈ [1, 2, 3] → 3 ∈ [1, 2, 4]h4:¬3 ∈ [1, 2, 4]⊢ 3 = 3; All goals completed! 🐙 end Perm3

9.3. Exercises with Inductive Relations🔗

namespace LePlayground

9.3.1. More Facts about Le🔗

Recall the Le relation from earlier in this chapter. Here are a number of facts about the ≤, <, and ≥ relations, and about Le's relationship to the boolean function Nat.ble, that we are going to need later in the course; the proofs make good practice for the case-analysis and induction techniques from the last few sections.

9.3.1.1. Facts about ≤🔗

Exercise★★★(le_facts)
theorem le_trans (m n k : Nat) (h₁ : m ≤ n) (h₂ : n ≤ k) : m ≤ k := m:Natn:Natk:Nath₁:m ≤ nh₂:n ≤ k⊢ m ≤ k solution! induction h₂ with m:Natn:Natk:Nath₁:m ≤ n⊢ m ≤ n All goals completed! 🐙 m:Natn:Natk:Nath₁:m ≤ nm✝:Nath✝:n ≤ m✝ih:m ≤ m✝⊢ m ≤ m✝ + 1 m:Natn:Natk:Nath₁:m ≤ nm✝:Nath✝:n ≤ m✝ih:m ≤ m✝⊢ m ≤ m✝; All goals completed! 🐙 theorem zero_le (n : Nat) : 0 ≤ n := n:Nat⊢ 0 ≤ n solution! induction n with ⊢ 0 ≤ 0 All goals completed! 🐙 n:Natih:0 ≤ n⊢ 0 ≤ n + 1 n:Natih:0 ≤ n⊢ 0 ≤ n; All goals completed! 🐙 theorem succ_le_succ (n m : Nat) (h : n ≤ m) : n + 1 ≤ m + 1 := n:Natm:Nath:n ≤ m⊢ n + 1 ≤ m + 1 solution! induction h with n:Natm:Nat⊢ n + 1 ≤ n + 1 All goals completed! 🐙 n:Natm:Natm✝:Nath:n ≤ m✝ih:n + 1 ≤ m✝ + 1⊢ n + 1 ≤ m✝ + 1 + 1 n:Natm:Natm✝:Nath:n ≤ m✝ih:n + 1 ≤ m✝ + 1⊢ n + 1 ≤ (m✝ + 1).succ n:Natm:Natm✝:Nath:n ≤ m✝ih:n + 1 ≤ m✝ + 1⊢ n + 1 ≤ m✝ + 1; All goals completed! 🐙 theorem le_of_succ_le_succ (n m : Nat) (h : n + 1 ≤ m + 1) : n ≤ m := n:Natm:Nath:n + 1 ≤ m + 1⊢ n ≤ m solution! inversion h with | refl => All goals completed! 🐙 | step h' => n:Natm:Nath':n + 1 ≤ m⊢ n ≤ n + 1n:Natm:Nath':n + 1 ≤ m⊢ n + 1 ≤ m n:Natm:Nath':n + 1 ≤ m⊢ n ≤ n + 1 n:Natm:Nath':n + 1 ≤ m⊢ n ≤ n; All goals completed! 🐙 n:Natm:Nath':n + 1 ≤ m⊢ n + 1 ≤ m All goals completed! 🐙 theorem le_not_succ_le_self (n : Nat) : ¬ (n + 1 ≤ n) := n:Nat⊢ ¬n + 1 ≤ n solution! induction n with ⊢ ¬0 + 1 ≤ 0 h:0 + 1 ≤ 0⊢ False; All goals completed! 🐙 n':Natih:¬n' + 1 ≤ n'⊢ ¬n' + 1 + 1 ≤ n' + 1 n':Natih:¬n' + 1 ≤ n'h:n' + 1 + 1 ≤ n' + 1⊢ False n':Natih:¬n' + 1 ≤ n'h:n' + 1 + 1 ≤ n' + 1⊢ n' + 1 ≤ n' n':Natih:¬n' + 1 ≤ n'h:n' + 1 + 1 ≤ n' + 1⊢ n' + 1 + 1 ≤ n' + 1 All goals completed! 🐙 theorem le_add_right (n m : Nat) : n ≤ n + m := n:Natm:Nat⊢ n ≤ n + m solution! induction n with m:Nat⊢ 0 ≤ 0 + m m:Nat⊢ 0 ≤ m; All goals completed! 🐙 m:Natn':Natih:n' ≤ n' + m⊢ n' + 1 ≤ n' + 1 + m m:Natn':Natih:n' ≤ n' + m⊢ n' + 1 ≤ (n' + m).succ m:Natn':Natih:n' ≤ n' + m⊢ n' ≤ n' + m All goals completed! 🐙
Exercise★★(le_facts1)
theorem le_and_le_of_add_le (n₁ n₂ m : Nat) (h : n₁ + n₂ ≤ m) : n₁ ≤ m ∧ n₂ ≤ m := n₁:Natn₂:Natm:Nath:n₁ + n₂ ≤ m⊢ n₁ ≤ m ∧ n₂ ≤ m solution! induction h with n₁:Natn₂:Natm:Nat⊢ n₁ ≤ n₁ + n₂ ∧ n₂ ≤ n₁ + n₂ n₁:Natn₂:Natm:Nat⊢ n₁ ≤ n₁ + n₂n₁:Natn₂:Natm:Nat⊢ n₂ ≤ n₁ + n₂ n₁:Natn₂:Natm:Nat⊢ n₁ ≤ n₁ + n₂ All goals completed! 🐙 n₁:Natn₂:Natm:Nat⊢ n₂ ≤ n₁ + n₂ n₁:Natn₂:Natm:Nat⊢ n₂ ≤ n₂ + n₁; All goals completed! 🐙 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ ≤ m✝ + 1 ∧ n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ ≤ m✝ + 1n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ ≤ n₁ + n₂n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ + n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ ≤ n₁ + n₂ All goals completed! 🐙 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ + n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ + n₂ ≤ m✝; All goals completed! 🐙 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₂ ≤ n₁ + n₂n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ + n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₂ ≤ n₁ + n₂ n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₂ ≤ n₂ + n₁; All goals completed! 🐙 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ + n₂ ≤ m✝ + 1 n₁:Natn₂:Natm:Natm✝:Nath✝:n₁ + n₂ ≤ m✝h_ih✝:n₁ ≤ m✝ ∧ n₂ ≤ m✝⊢ n₁ + n₂ ≤ m✝; All goals completed! 🐙 theorem le_or_le_of_add_le_add (n m p q : Nat) (h : n + m ≤ p + q) : n ≤ p ∨ m ≤ q := n:Natm:Natp:Natq:Nath:n + m ≤ p + q⊢ n ≤ p ∨ m ≤ q /- Hint: May be easiest to prove by induction on `n`. -/ solution! induction n generalizing m p q with m:Natp:Natq:Nath:0 + m ≤ p + q⊢ 0 ≤ p ∨ m ≤ q m:Natp:Natq:Nath:0 + m ≤ p + q⊢ 0 ≤ p; All goals completed! 🐙 n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natp:Natq:Nath:n' + 1 + m ≤ p + q⊢ n' + 1 ≤ p ∨ m ≤ q cases p with n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Nath:n' + 1 + m ≤ 0 + q⊢ n' + 1 ≤ 0 ∨ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Nath:n' + 1 + m ≤ 0 + q⊢ m ≤ q; n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Nath:n' + 1 ≤ 0 + q ∧ m ≤ 0 + q⊢ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Nath✝:n' + 1 ≤ 0 + q ∧ m ≤ 0 + qleft✝:n' + 1 ≤ 0 + qh:m ≤ 0 + q⊢ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Nath✝:n' + 1 ≤ 0 + q ∧ m ≤ 0 + qleft✝:n' + 1 ≤ 0 + qh:m ≤ q⊢ m ≤ q; All goals completed! 🐙 n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath:n' + 1 + m ≤ p' + 1 + q⊢ n' + 1 ≤ p' + 1 ∨ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath:(n' + m).succ ≤ (p' + q).succ⊢ n' + 1 ≤ p' + 1 ∨ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath:n' + m ≤ p' + q⊢ n' + 1 ≤ p' + 1 ∨ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath:n' ≤ p' ∨ m ≤ q⊢ n' + 1 ≤ p' + 1 ∨ m ≤ q cases h with n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath✝:n' ≤ p'⊢ n' + 1 ≤ p' + 1 ∨ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath✝:n' ≤ p'⊢ n' + 1 ≤ p' + 1; n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath✝:n' ≤ p'⊢ n' ≤ p'; All goals completed! 🐙 n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath✝:m ≤ q⊢ n' + 1 ≤ p' + 1 ∨ m ≤ q n':Natih:∀ (m p q : Nat), n' + m ≤ p + q → n' ≤ p ∨ m ≤ qm:Natq:Natp':Nath✝:m ≤ q⊢ m ≤ q; All goals completed! 🐙
Exercise★★(plus_le_facts2)
theorem add_le_add_left (n m p : Nat) (h : n ≤ m) : p + n ≤ p + m := n:Natm:Natp:Nath:n ≤ m⊢ p + n ≤ p + m solution! induction p with n:Natm:Nath:n ≤ m⊢ 0 + n ≤ 0 + m n:Natm:Nath:n ≤ m⊢ n ≤ m; All goals completed! 🐙 n:Natm:Nath:n ≤ mp':Natih:p' + n ≤ p' + m⊢ p' + 1 + n ≤ p' + 1 + m n:Natm:Nath:n ≤ mp':Natih:p' + n ≤ p' + m⊢ (p' + n).succ ≤ (p' + m).succ n:Natm:Nath:n ≤ mp':Natih:p' + n ≤ p' + m⊢ p' + n ≤ p' + m All goals completed! 🐙 theorem add_le_add_right (n m p : Nat) (h : n ≤ m) : n + p ≤ m + p := n:Natm:Natp:Nath:n ≤ m⊢ n + p ≤ m + p solution! n:Natm:Natp:Nath:n ≤ m⊢ p + n ≤ p + m n:Natm:Natp:Nath:n ≤ m⊢ n ≤ m All goals completed! 🐙 theorem le_add_right_of_le (n m p : Nat) (h : n ≤ m) : n ≤ m + p := n:Natm:Natp:Nath:n ≤ m⊢ n ≤ m + p solution! induction p with n:Natm:Nath:n ≤ m⊢ n ≤ m + 0 n:Natm:Nath:n ≤ m⊢ n ≤ m; All goals completed! 🐙 n:Natm:Nath:n ≤ mp':Natih:n ≤ m + p'⊢ n ≤ m + (p' + 1) n:Natm:Nath:n ≤ mp':Natih:n ≤ m + p'⊢ n ≤ m + p' + 1; n:Natm:Nath:n ≤ mp':Natih:n ≤ m + p'⊢ n ≤ m + p'; All goals completed! 🐙

9.3.1.2. Facts about < and ≥🔗

The "strictly less than" relation n < m can now be defined in terms of Le.

def Lt (n m : Nat) : Prop := Le (n + 1) m scoped infix:50 (priority := high) " < " => Lt

The ≥ relation is defined in terms of ≤, so unfolding its definition with rw lets us move between them.

def Ge (m n : Nat) : Prop := Le n m scoped infix:50 (priority := high) " ≥ " => Ge example (m n : Nat) (h : m ≥ n) : n ≤ m := m:Natn:Nath:m ≥ n⊢ n ≤ m m:Natn:Nath:n ≤ m⊢ n ≤ m All goals completed! 🐙
Exercise★★★(lt_facts) (Optional)
theorem lt_not_lt_zero (n : Nat) : ¬ n < 0 := n:Nat⊢ ¬n < 0 n:Nath:n < 0⊢ False All goals completed! 🐙 theorem lt_or_ge (n m : Nat) : n < m ∨ n ≥ m := n:Natm:Nat⊢ n < m ∨ n ≥ m solution! induction n generalizing m with m:Nat⊢ 0 < m ∨ 0 ≥ m cases m with ⊢ 0 < 0 ∨ 0 ≥ 0 ⊢ 0 ≥ 0; All goals completed! 🐙 n✝:Nat⊢ 0 < n✝ + 1 ∨ 0 ≥ n✝ + 1 n✝:Nat⊢ 0 < n✝ + 1; n✝:Nat⊢ 0 ≤ n✝; All goals completed! 🐙 n':Natih:∀ (m : Nat), n' < m ∨ n' ≥ mm:Nat⊢ n' + 1 < m ∨ n' + 1 ≥ m cases m with n':Natih:∀ (m : Nat), n' < m ∨ n' ≥ m⊢ n' + 1 < 0 ∨ n' + 1 ≥ 0 n':Natih:∀ (m : Nat), n' < m ∨ n' ≥ m⊢ n' + 1 < 0 ∨ 0 ≤ n' + 1; n':Natih:∀ (m : Nat), n' < m ∨ n' ≥ m⊢ 0 ≤ n' + 1 All goals completed! 🐙 n':Natih:∀ (m : Nat), n' < m ∨ n' ≥ mm':Nat⊢ n' + 1 < m' + 1 ∨ n' + 1 ≥ m' + 1 cases ih m' with n':Natih✝:∀ (m : Nat), n' < m ∨ n' ≥ mm':Natih:n' < m'⊢ n' + 1 < m' + 1 ∨ n' + 1 ≥ m' + 1 n':Natih✝:∀ (m : Nat), n' < m ∨ n' ≥ mm':Natih:n' < m'⊢ n' + 1 < m' + 1 n':Natih✝:∀ (m : Nat), n' < m ∨ n' ≥ mm':Natih:n' < m'⊢ n' + 1 ≤ m' All goals completed! 🐙 n':Natih✝:∀ (m : Nat), n' < m ∨ n' ≥ mm':Natih:n' ≥ m'⊢ n' + 1 < m' + 1 ∨ n' + 1 ≥ m' + 1 n':Natih✝:∀ (m : Nat), n' < m ∨ n' ≥ mm':Natih:n' ≥ m'⊢ n' + 1 ≥ m' + 1 n':Natih✝:∀ (m : Nat), n' < m ∨ n' ≥ mm':Natih:n' ≥ m'⊢ m' ≤ n' All goals completed! 🐙 theorem le_of_lt (n m : Nat) (h : n < m) : n ≤ m := n:Natm:Nath:n < m⊢ n ≤ m solution! n:Natm:Nath:n < m⊢ n + 1 ≤ m + 1 n:Natm:Nath:n < m⊢ n + 1 ≤ m; All goals completed! 🐙 theorem lt_and_lt_of_add_lt (n₁ n₂ m : Nat) (h : n₁ + n₂ < m) : n₁ < m ∧ n₂ < m := n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ < m ∧ n₂ < m solution! n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ < mn₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ < m n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ < m n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ + 1 ≤ n₁ + n₂ + 1n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ + n₂ + 1 ≤ m n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ + 1 ≤ n₁ + n₂ + 1 n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ ≤ n₁ + n₂ All goals completed! 🐙 n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ + n₂ + 1 ≤ m All goals completed! 🐙 n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ < m n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ + 1 ≤ n₂ + n₁ + 1n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ + n₁ + 1 ≤ m n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ + 1 ≤ n₂ + n₁ + 1 n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ ≤ n₂ + n₁ All goals completed! 🐙 n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₂ + n₁ + 1 ≤ m n₁:Natn₂:Natm:Nath:n₁ + n₂ < m⊢ n₁ + n₂ + 1 ≤ m; All goals completed! 🐙

9.3.1.3. Relating Le and Nat.ble🔗

Recall that Le and Nat.ble are equivalent (as promised earlier).

Exercise★★★★(ble) (Optional)
theorem le_of_ble_eq_true (n m : Nat) (h : Nat.ble n m = true) : n ≤ m := n:Natm:Nath:n.ble m = true⊢ n ≤ m solution! induction n generalizing m with m:Nath:Nat.ble 0 m = true⊢ 0 ≤ m All goals completed! 🐙 n':Natih:∀ (m : Nat), n'.ble m = true → n' ≤ mm:Nath:(n' + 1).ble m = true⊢ n' + 1 ≤ m cases m with n':Natih:∀ (m : Nat), n'.ble m = true → n' ≤ mh:(n' + 1).ble 0 = true⊢ n' + 1 ≤ 0 All goals completed! 🐙 n':Natih:∀ (m : Nat), n'.ble m = true → n' ≤ mm':Nath:(n' + 1).ble (m' + 1) = true⊢ n' + 1 ≤ m' + 1 n':Natih:∀ (m : Nat), n'.ble m = true → n' ≤ mm':Nath:n'.ble m' = true⊢ n' + 1 ≤ m' + 1 n':Natih:∀ (m : Nat), n'.ble m = true → n' ≤ mm':Nath:n'.ble m' = true⊢ n' ≤ m' n':Natih:∀ (m : Nat), n'.ble m = true → n' ≤ mm':Nath:n'.ble m' = true⊢ n'.ble m' = true; All goals completed! 🐙 theorem ble_eq_true_of_le n m (h : n ≤ m) : Nat.ble n m = true := n:Natm:Nath:n ≤ m⊢ n.ble m = true solution! induction n generalizing m with m:Nath:0 ≤ m⊢ Nat.ble 0 m = true All goals completed! 🐙 n':Natih:∀ (m : Nat), n' ≤ m → n'.ble m = truem:Nath:n' + 1 ≤ m⊢ (n' + 1).ble m = true cases m with n':Natih:∀ (m : Nat), n' ≤ m → n'.ble m = trueh:n' + 1 ≤ 0⊢ (n' + 1).ble 0 = true All goals completed! 🐙 n':Natih:∀ (m : Nat), n' ≤ m → n'.ble m = truem':Nath:n' + 1 ≤ m' + 1⊢ (n' + 1).ble (m' + 1) = true n':Natih:∀ (m : Nat), n' ≤ m → n'.ble m = truem':Nath:n' + 1 ≤ m' + 1⊢ n'.ble m' = true n':Natih:∀ (m : Nat), n' ≤ m → n'.ble m = truem':Nath:n' ≤ m'⊢ n'.ble m' = true n':Natih:∀ (m : Nat), n' ≤ m → n'.ble m = truem':Nath:n'.ble m' = true⊢ n'.ble m' = true All goals completed! 🐙

Hint: The next two can easily be proved without using induction.

theorem ble_eq (n m : Nat) : Nat.ble n m = true ↔ n ≤ m := n:Natm:Nat⊢ n.ble m = true ↔ n ≤ m solution! n:Natm:Nat⊢ n.ble m = true → n ≤ mn:Natm:Nat⊢ n ≤ m → n.ble m = true n:Natm:Nat⊢ n.ble m = true → n ≤ m All goals completed! 🐙 n:Natm:Nat⊢ n ≤ m → n.ble m = true All goals completed! 🐙 theorem ble_trans (n m k : Nat) : Nat.ble n m = true → Nat.ble m k = true → Nat.ble n k = true := n:Natm:Natk:Nat⊢ n.ble m = true → m.ble k = true → n.ble k = true solution! n:Natm:Natk:Nat⊢ n ≤ m → m ≤ k → n ≤ k All goals completed! 🐙
end LePlayground
Exercise★★★(R_provability) (Manually graded)

We can define three-place relations, four-place relations, etc., in just the same way as binary relations. For example, consider the following three-place relation on numbers:

namespace RProvability inductive R : Nat → Nat → Nat → Prop where | c1 : R 0 0 0 | c2 {m n k : Nat} (h : R m n k) : R (m + 1) n (k + 1) | c3 {m n k : Nat} (h : R m n k) : R m (n + 1) (k + 1) | c4 {m n k : Nat} (h : R (m + 1) (n + 1) (k + 2)) : R m n k | c5 {m n k : Nat} (h : R m n k) : R n m k
  1. Which of the following propositions are provable?

  • R 1 1 2

  • R 2 2 6

  1. If we dropped constructor c5 from the definition of R, would the set of provable propositions change? Briefly (one sentence) explain your answer.

  2. If we dropped constructor c4 from the definition of R, would the set of provable propositions change? Briefly (one sentence) explain your answer.

  1. The first proposition is provable and the second is not.

example : R 1 1 2 := ⊢ R 1 1 2 ⊢ R 0 1 1 ⊢ R 0 0 0 All goals completed! 🐙

The key invariant here is that whenever R m n k holds, we must have k = m + n. We can prove this invariant as follows:

theorem R.eq_add {m n k : Nat} (h : R m n k) : k = m + n := m:Natn:Natk:Nath:R m n k⊢ k = m + n induction h with All goals completed! 🐙

lia helps us solve linear arithmetic goals. We'll learn more about it in the Automation chapter.

Now we can disprove the second proposition:

example : ¬ R 2 2 6 := ⊢ ¬R 2 2 6 h:R 2 2 6⊢ False h:6 = 2 + 2⊢ False All goals completed! 🐙
  1. Dropping c5 would not change the set of provable propositions. c4 and c1 don't interact with c5, since they're already symmetric in m and n; c2 followed by c5 is equivalent to c3, and vice versa.

  2. Dropping c4 would not change the set of provable propositions. This constructor just "undoes" one application of c2 and one application of c3. More precisely, the only way we can construct evidence for R (m + 1) (n + 1) (k + 2) is by applying c2 and c3 (in either order) to evidence for R m n k, so the latter must already hold. (This can be proved by induction, although the proof is surprisingly tedious.)

We can prove c4 and c5 are redundant by redefining R' with only c1, c2, and c3, and proving R' is equivalent to R.

Another useful fact is that the converse of the above invariant, R.eq_add, is also true:

theorem R.of_eq_add {m n k : Nat} (h : k = m + n) : R m n k := m:Natn:Natk:Nath:k = m + n⊢ R m n k m:Natn:Nat⊢ R m n (m + n) induction n with m:Nat⊢ R m 0 (m + 0) induction m with ⊢ R 0 0 (0 + 0) All goals completed! 🐙 n:Nata✝:R n 0 (n + 0)⊢ R (n + 1) 0 (n + 1 + 0) n:Nata✝:R n 0 (n + 0)⊢ R n 0 n; All goals completed! 🐙 m:Natn:Nata✝:R m n (m + n)⊢ R m (n + 1) (m + (n + 1)) m:Natn:Nata✝:R m n (m + n)⊢ R m n (m.add n); All goals completed! 🐙

Now we are ready to define R'.

inductive R' : Nat → Nat → Nat → Prop where | c1 : R' 0 0 0 | c2 m n k (h : R' m n k) : R' (m + 1) n (k + 1) | c3 m n k (h : R' m n k) : R' m (n + 1) (k + 1) theorem R'.c5 {m n k : Nat} (h : R' m n k) : R' n m k := m:Natn:Natk:Nath:R' m n k⊢ R' n m k induction h with m:Natn:Natk:Nat⊢ R' 0 0 0 All goals completed! 🐙 m✝:Natn✝:Natk:Natm:Natn:Nato:Nath:R' m n oih:R' n m o⊢ R' n (m + 1) (o + 1) m✝:Natn✝:Natk:Natm:Natn:Nato:Nath:R' m n oih:R' n m o⊢ R' n m o; All goals completed! 🐙 m✝:Natn✝:Natk:Natm:Natn:Nato:Nath:R' m n oih:R' n m o⊢ R' (n + 1) m (o + 1) m✝:Natn✝:Natk:Natm:Natn:Nato:Nath:R' m n oih:R' n m o⊢ R' n m o; All goals completed! 🐙 theorem R'.eq_add {m n k : Nat} (h : R' m n k) : k = m + n := m:Natn:Natk:Nath:R' m n k⊢ k = m + n induction h with All goals completed! 🐙 theorem R'.of_eq_add {m n : Nat} : R' m n (m + n) := m:Natn:Nat⊢ R' m n (m + n) induction n with m:Nat⊢ R' m 0 (m + 0) induction m with ⊢ R' 0 0 (0 + 0) All goals completed! 🐙 n:Nata✝:R' n 0 (n + 0)⊢ R' (n + 1) 0 (n + 1 + 0) n:Nata✝:R' n 0 (n + 0)⊢ R' n 0 n; All goals completed! 🐙 m:Natn:Nata✝:R' m n (m + n)⊢ R' m (n + 1) (m + (n + 1)) m:Natn:Nata✝:R' m n (m + n)⊢ R' m n (m.add n); All goals completed! 🐙 theorem R'.c4 {m n k : Nat} (h : R' (m + 1) (n + 1) (k + 2)) : R' m n k := m:Natn:Natk:Nath:R' (m + 1) (n + 1) (k + 2)⊢ R' m n k m:Natn:Natk:Nath:R' (m + 1) (n + 1) (k + 2)this:k + 2 = m + 1 + (n + 1)⊢ R' m n k m:Natn:Natk:Nath:R' (m + 1) (n + 1) (k + 2)this:k + 2 = m + 1 + (n + 1)hk:k = m + n⊢ R' m n k m:Natn:Nath:R' (m + 1) (n + 1) (m + n + 2)this:m + n + 2 = m + 1 + (n + 1)⊢ R' m n (m + n) All goals completed! 🐙 theorem R'.iff_R {m n k : Nat} : R' m n k ↔ R m n k := m:Natn:Natk:Nat⊢ R' m n k ↔ R m n k m:Natn:Natk:Nat⊢ R' m n k → R m n km:Natn:Natk:Nat⊢ R m n k → R' m n k m:Natn:Natk:Nat⊢ R' m n k → R m n k m:Natn:Natk:Nath:R' m n k⊢ R m n k induction h with m:Natn:Natk:Nat⊢ R 0 0 0 All goals completed! 🐙 m✝:Natn✝:Natk✝:Natm:Natn:Natk:Nath:R' m n kih:R m n k⊢ R (m + 1) n (k + 1) m✝:Natn✝:Natk✝:Natm:Natn:Natk:Nath:R' m n kih:R m n k⊢ R m n k; All goals completed! 🐙 m✝:Natn✝:Natk✝:Natm:Natn:Natk:Nath:R' m n kih:R m n k⊢ R m (n + 1) (k + 1) m✝:Natn✝:Natk✝:Natm:Natn:Natk:Nath:R' m n kih:R m n k⊢ R m n k; All goals completed! 🐙 m:Natn:Natk:Nat⊢ R m n k → R' m n k m:Natn:Natk:Nath:R m n k⊢ R' m n k induction h with m:Natn:Natk:Nat⊢ R' 0 0 0 All goals completed! 🐙 m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R m✝ n✝ k✝ih:R' m✝ n✝ k✝⊢ R' (m✝ + 1) n✝ (k✝ + 1) m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R m✝ n✝ k✝ih:R' m✝ n✝ k✝⊢ R' m✝ n✝ k✝; All goals completed! 🐙 m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R m✝ n✝ k✝ih:R' m✝ n✝ k✝⊢ R' m✝ (n✝ + 1) (k✝ + 1) m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R m✝ n✝ k✝ih:R' m✝ n✝ k✝⊢ R' m✝ n✝ k✝; All goals completed! 🐙 m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R (m✝ + 1) (n✝ + 1) (k✝ + 2)ih:R' (m✝ + 1) (n✝ + 1) (k✝ + 2)⊢ R' m✝ n✝ k✝ m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R (m✝ + 1) (n✝ + 1) (k✝ + 2)ih:R' (m✝ + 1) (n✝ + 1) (k✝ + 2)⊢ R' (m✝ + 1) (n✝ + 1) (k✝ + 2); All goals completed! 🐙 m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R m✝ n✝ k✝ih:R' m✝ n✝ k✝⊢ R' n✝ m✝ k✝ m:Natn:Natk:Natm✝:Natn✝:Natk✝:Nath:R m✝ n✝ k✝ih:R' m✝ n✝ k✝⊢ R' m✝ n✝ k✝; All goals completed! 🐙
Exercise★★★(R_fact) (Optional)

The relation R above actually encodes a familiar function. Figure out which function; then state and prove this equivalence in Lean.

def funR : Nat → Nat → Nat := solution!(fun m n => m + n) theorem funR_iff_R {m n k : Nat} : funR m n = k ↔ R m n k := m:Natn:Natk:Nat⊢ funR m n = k ↔ R m n k solution! m:Natn:Natk:Nat⊢ funR m n = k → R m n km:Natn:Natk:Nat⊢ R m n k → funR m n = k m:Natn:Natk:Nat⊢ funR m n = k → R m n k m:Natn:Natk:Nath:funR m n = k⊢ R m n k m:Natn:Natk:Nath:m + n = k⊢ R m n k m:Natn:Nat⊢ R m n (m + n) m:Natn:Nat⊢ m + n = m + n All goals completed! 🐙 m:Natn:Natk:Nat⊢ R m n k → funR m n = k m:Natn:Natk:Nath:R m n k⊢ funR m n = k m:Natn:Natk:Nath:R m n kthis:k = m + n⊢ funR m n = k All goals completed! 🐙 end RProvability
Exercise★★★★(subsequence) (Advanced)

A list is a subsequence of another list if all of the elements in the first list occur in the same order in the second list, possibly with some extra elements in between. For example,

[1, 2, 3]

is a subsequence of each of the lists

[1, 2, 3]
[1, 1, 1, 2, 2, 3]
[1, 2, 7, 3]
[5, 6, 1, 9, 9, 2, 7, 3, 8]

but it is not a subsequence of any of the lists

[1, 2]
[1, 3]
[5, 6, 2, 1, 7, 3, 8].
  • Define an inductive proposition Subseq on List Nat that captures what it means to be a subsequence. There are a number of correct ways to do this. You should make sure that your definition behaves correctly on all the positive and negative examples above, but you do not need to prove this formally.

  • Prove Subseq.refl that subsequence is reflexive — that is, any list is a subsequence of itself.

  • Prove Subseq.append that for any lists l₁, l₂, and l₃, if l₁ is a subsequence of l₂, then l₁ is also a subsequence of l₂ ++ l₃.

  • (Harder) Prove Subseq.trans that subsequence is transitive — that is, if l₁ is a subsequence of l₂ and l₂ is a subsequence of l₃, then l₁ is a subsequence of l₃.

Note to developers (before next release)
AC'21: I think that it is more atomic to consider
[sub_nil : subseq [] []]. The benefits is that it makes calls to
[inversion] produce fewer goals. The downside is that one has to
state as a lemma [sub_nil_l : forall l, subseq [] l], however it
would be nice to have this as an exercise anyway, because otherwise
students who go for the definition of [sub_seq [] []] are required
to guess the need for [sub_nil_l].
BCP: I agree this version could be nicer to suggest, and I agree that
adding this lemma as a warm-up exercise is nice.

Sainati 25: I am generally not against proofs that can be made much easier with smart inductive definitions (this is sort of the whole ball game in a way, isn't it?) but one way to make sure students can't trivialize the exercise is to just give them the definition we want them to use? We could also add a (maybe optional) question afterwards to provide a different definition that makes the proofs easier (and maybe prove them equivalent).

inductive Subseq : List Nat → List Nat → Prop where | nil {l : List Nat} : Subseq [] l | take {x : Nat} {l₁ l₂ : List Nat} (h : Subseq l₁ l₂) : Subseq (x :: l₁) (x :: l₂) | skip {x : Nat} {l₁ l₂ : List Nat} (h : Subseq l₁ l₂) : Subseq l₁ (x :: l₂) namespace Subseq theorem refl (l : List Nat) : Subseq l l := l:List Nat⊢ Subseq l l solution! induction l with ⊢ Subseq [] [] All goals completed! 🐙 x:Natxs:List Natih:Subseq xs xs⊢ Subseq (x :: xs) (x :: xs) x:Natxs:List Natih:Subseq xs xs⊢ Subseq xs xs; All goals completed! 🐙 theorem append (l₁ l₂ l₃ : List Nat) (h : Subseq l₁ l₂) : Subseq l₁ (l₂ ++ l₃) := l₁:List Natl₂:List Natl₃:List Nath:Subseq l₁ l₂⊢ Subseq l₁ (l₂ ++ l₃) solution! induction h with l₁:List Natl₂:List Natl₃:List Natl✝:List Nat⊢ Subseq [] (l✝ ++ l₃) All goals completed! 🐙 l₁:List Natl₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝h_ih✝:Subseq l₁✝ (l₂✝ ++ l₃)⊢ Subseq (x✝ :: l₁✝) (x✝ :: l₂✝ ++ l₃) l₁:List Natl₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝h_ih✝:Subseq l₁✝ (l₂✝ ++ l₃)⊢ Subseq l₁✝ (l₂✝.append l₃); All goals completed! 🐙 l₁:List Natl₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝h_ih✝:Subseq l₁✝ (l₂✝ ++ l₃)⊢ Subseq l₁✝ (x✝ :: l₂✝ ++ l₃) l₁:List Natl₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝h_ih✝:Subseq l₁✝ (l₂✝ ++ l₃)⊢ Subseq l₁✝ (l₂✝.append l₃); All goals completed! 🐙 theorem trans (l₁ l₂ l₃ : List Nat) (h12 : Subseq l₁ l₂) (h23 : Subseq l₂ l₃) : Subseq l₁ l₃ := l₁:List Natl₂:List Natl₃:List Nath12:Subseq l₁ l₂h23:Subseq l₂ l₃⊢ Subseq l₁ l₃ /- Hint: be careful about what you are doing induction on and which other things need to be generalized... -/ solution! induction h23 generalizing l₁ with l₂:List Natl₃:List Natl✝:List Natl₁:List Nath12:Subseq l₁ []⊢ Subseq l₁ l✝ l₂:List Natl₃:List Natl✝:List Nat⊢ Subseq [] l✝; All goals completed! 🐙 l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath12:Subseq l₁ (x✝ :: l₁✝)⊢ Subseq l₁ (x✝ :: l₂✝) l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝⊢ Subseq [] (x✝ :: l₂✝)l₂:List Natl₃:List Natx✝:Natl₁✝¹:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁✝:List Nath✝:Subseq l₁✝ l₁✝¹⊢ Subseq (x✝ :: l₁✝) (x✝ :: l₂✝)l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath✝:Subseq l₁ l₁✝⊢ Subseq l₁ (x✝ :: l₂✝); l₂:List Natl₃:List Natx✝:Natl₁✝¹:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁✝:List Nath✝:Subseq l₁✝ l₁✝¹⊢ Subseq (x✝ :: l₁✝) (x✝ :: l₂✝)l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath✝:Subseq l₁ l₁✝⊢ Subseq l₁ (x✝ :: l₂✝) l₂:List Natl₃:List Natx✝:Natl₁✝¹:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁✝:List Nath✝:Subseq l₁✝ l₁✝¹⊢ Subseq (x✝ :: l₁✝) (x✝ :: l₂✝) l₂:List Natl₃:List Natx✝:Natl₁✝¹:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁✝:List Nath✝:Subseq l₁✝ l₁✝¹⊢ Subseq l₁✝ l₂✝; l₂:List Natl₃:List Natx✝:Natl₁✝¹:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁✝:List Nath✝:Subseq l₁✝ l₁✝¹⊢ Subseq l₁✝ l₁✝¹; All goals completed! 🐙 l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath✝:Subseq l₁ l₁✝⊢ Subseq l₁ (x✝ :: l₂✝) l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath✝:Subseq l₁ l₁✝⊢ Subseq l₁ l₂✝; l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝¹:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath✝:Subseq l₁ l₁✝⊢ Subseq l₁ l₁✝; All goals completed! 🐙 l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath12:Subseq l₁ l₁✝⊢ Subseq l₁ (x✝ :: l₂✝) l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath12:Subseq l₁ l₁✝⊢ Subseq l₁ l₂✝; l₂:List Natl₃:List Natx✝:Natl₁✝:List Natl₂✝:List Nath✝:Subseq l₁✝ l₂✝ih:∀ (l₁ : List Nat), Subseq l₁ l₁✝ → Subseq l₁ l₂✝l₁:List Nath12:Subseq l₁ l₁✝⊢ Subseq l₁ l₁✝; All goals completed! 🐙 end Subseq
Exercise★★(R_provability2) (Optional, Manually graded)

Suppose we give Lean the following definition:

namespace RProvability2 inductive R : Nat → List Nat → Prop where | c1 : R 0 [] | c2 {n : Nat} {l : List Nat} (h : R n l) : R (n + 1) (n :: l) | c3 {n : Nat} {l : List Nat} (h : R (n + 1) l) : R n l

Which of the following propositions are provable?

  • R 2 [1, 0]

  • R 1 [1, 2, 1, 0]

  • R 6 [3, 2, 1, 0]

The first two are provable, the third is not.

example : R 2 [1, 0] := ⊢ R 2 [1, 0] ⊢ R 1 [0] ⊢ R 0 [] All goals completed! 🐙 example : R 1 [1, 2, 1, 0] := ⊢ R 1 [1, 2, 1, 0] ⊢ R (1 + 1) [1, 2, 1, 0] ⊢ R 1 [2, 1, 0] ⊢ R (1 + 1) [2, 1, 0] ⊢ R (1 + 1 + 1) [2, 1, 0] ⊢ R (1 + 1) [1, 0] ⊢ R 1 [0] ⊢ R 0 [] All goals completed! 🐙

In case this question puzzled you, one good way to understand definitions like this is to explore their implications with concrete examples, e.g.

R 0 []           by c1
R 1 [0]          by c2 using R 0 []
R 2 [1, 0]       by c2 using R 1 [0]
R 3 [2, 1, 0]    by c2 using R 2 [1, 0]
R 2 [2, 1, 0]    by c3 using R 3 [2, 1, 0]
R 1 [2, 1, 0]    by c3 using R 2 [2, 1, 0]
R 2 [1, 2, 1, 0] by c2 using R 1 [2, 1, 0]
R 1 [1, 2, 1, 0] by c3 using R 2 [1, 2, 1, 0]
etc.

If you do a few more of these yourself, you should see the pattern emerging.

end RProvability2
Exercise★★(total_relation) (Optional)

Define an inductive binary relation TotalRelation that holds between every pair of natural numbers.

inductive TotalRelation : Nat → Nat → Prop where | tot (n m : Nat) : TotalRelation n m theorem total_relation_is_total (n m : Nat) : TotalRelation n m := n:Natm:Nat⊢ TotalRelation n m solution! All goals completed! 🐙
Exercise★★(empty_relation) (Optional)

Define an inductive binary relation EmptyRelation (on numbers) that never holds.

inductive EmptyRelation : Nat → Nat → Prop where theorem empty_relation_is_empty (n m : Nat) : ¬ EmptyRelation n m := n:Natm:Nat⊢ ¬EmptyRelation n m solution! n:Natm:Natcontra:EmptyRelation n m⊢ False; All goals completed! 🐙

9.4. Additional Exercises🔗

Exercise★★★(nostutter_defn) (Manually graded)

Formulating inductive definitions of properties is an important skill you'll need in this course. Try to solve this exercise without any help.

We say that a list "stutters" if it repeats the same element consecutively. (This is different from not containing duplicates: the sequence [1, 4, 1] has two occurrences of the element 1 but does not stutter.) The property NoStutter l means that l does not stutter. Formulate an inductive definition for NoStutter.

inductive NoStutter {α : Type} : List α → Prop where | nil : NoStutter [] | singleton {x : α} : NoStutter (x :: []) | cons {x y : α} {l : List α} (hneq : x ≠ y) (h : NoStutter (y :: l)) : NoStutter (x :: y :: l)

Make sure each of these tests succeeds, but feel free to change the suggested proof (in comments) if the given one doesn't work for you. Your definition might be different from ours and still be correct, in which case the examples might need a different proof. (You'll notice that the suggested proofs use a number of tactics we haven't talked about, to make them more robust to different possible ways of defining NoStutter. You can probably just uncomment and use them as is, but you can also prove each example with more basic tactics.)

example : NoStutter [3, 1, 4, 1, 5, 6] := ⊢ NoStutter [3, 1, 4, 1, 5, 6] suggested! ⊢ 3 ≠ 1⊢ NoStutter [1, 4, 1, 5, 6]; contra:3 = 1⊢ False⊢ NoStutter [1, 4, 1, 5, 6]; ⊢ NoStutter [1, 4, 1, 5, 6] ⊢ 1 ≠ 4⊢ NoStutter [4, 1, 5, 6]; contra:1 = 4⊢ False⊢ NoStutter [4, 1, 5, 6]; ⊢ NoStutter [4, 1, 5, 6] ⊢ 4 ≠ 1⊢ NoStutter [1, 5, 6]; contra:4 = 1⊢ False⊢ NoStutter [1, 5, 6]; ⊢ NoStutter [1, 5, 6] ⊢ 1 ≠ 5⊢ NoStutter [5, 6]; contra:1 = 5⊢ False⊢ NoStutter [5, 6]; ⊢ NoStutter [5, 6] ⊢ 5 ≠ 6⊢ NoStutter [6]; contra:5 = 6⊢ False⊢ NoStutter [6]; ⊢ NoStutter [6] All goals completed! 🐙 example : NoStutter (@List.nil Nat) := ⊢ NoStutter [] suggested! All goals completed! 🐙 example : NoStutter [5] := ⊢ NoStutter [5] suggested! All goals completed! 🐙 example : ¬ (NoStutter [3, 1, 1, 4]) := ⊢ ¬NoStutter [3, 1, 1, 4] suggested! contra:NoStutter [3, 1, 1, 4]⊢ False inversion contra with | cons contra => inversion contra with | cons _ h _ => All goals completed! 🐙
Exercise★★★★(filter_challenge) (Advanced)

Let's prove that our definition of filter from the Poly chapter matches an abstract specification. Here is the specification, written out informally in English:

A list l is an "in-order merge" of l₁ and l₂ if it contains all the same elements as l₁ and l₂, in the same order as l₁ and l₂, but possibly interleaved. For example,

[1, 4, 6, 2, 3]

is an in-order merge of

[1, 6, 2]

and

[4, 3].

Now, suppose we have a type α, a function test : α → Bool, and a list l of type List α. Suppose further that l is an in-order merge of two lists, l₁ and l₂, such that every item in l₁ satisfies test and no item in l₂ satisfies test. Then filter test l = l₁.

First define what it means for one list to be a merge of two others. Do this with an inductive relation, not a def.

inductive Merge {α : Type} : List α → List α → List α → Prop where | nil : Merge [] [] [] | left {x : α} {l₁ l₂ l₃ : List α} (h : Merge l₁ l₂ l₃) : Merge (x :: l₁) l₂ (x :: l₃) | right {x : α} {l₁ l₂ l₃ : List α} (h : Merge l₁ l₂ l₃) : Merge l₁ (x :: l₂) (x :: l₃) theorem merge_filter (α : Type) (test : α → Bool) (l l₁ l₂ : List α) (h : Merge l₁ l₂ l) (h₁ : l₁.allb test) (h₂ : l₂.allb (fun x => !test x)) : filter test l = l₁ := α:Typetest:α → Booll:List αl₁:List αl₂:List αh:Merge l₁ l₂ lh₁:List.allb test l₁ = trueh₂:List.allb (fun x => !test x) l₂ = true⊢ filter test l = l₁ solution! induction h with α:Typetest:α → Booll:List αl₁:List αl₂:List αh₁:List.allb test [] = trueh₂:List.allb (fun x => !test x) [] = true⊢ filter test [] = [] All goals completed! 🐙 α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test (x✝ :: l₁✝) = trueh₂:List.allb (fun x => !test x) l₂✝ = true⊢ filter test (x✝ :: l₃✝) = x✝ :: l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:test x✝ = true ∧ List.allb test l₁✝ = trueh₂:List.allb (fun x => !test x) l₂✝ = true⊢ filter test (x✝ :: l₃✝) = x✝ :: l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ filter test (x✝ :: l₃✝) = x✝ :: l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ x✝ :: filter test l₃✝ = x✝ :: l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ filter test l₃✝ = l₁✝; α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ List.allb test l₁✝ = trueα:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ List.allb (fun x => !test x) l₂✝ = true α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ List.allb test l₁✝ = true All goals completed! 🐙 α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₂:List.allb (fun x => !test x) l₂✝ = truehtest:test x✝ = trueh₁:List.allb test l₁✝ = true⊢ List.allb (fun x => !test x) l₂✝ = true All goals completed! 🐙 α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = trueh₂:List.allb (fun x => !test x) (x✝ :: l₂✝) = true⊢ filter test (x✝ :: l₃✝) = l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = trueh₂:test x✝ = false ∧ List.allb (fun x => !test x) l₂✝ = true⊢ filter test (x✝ :: l₃✝) = l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ filter test (x✝ :: l₃✝) = l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ filter test l₃✝ = l₁✝ α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ filter test l₃✝ = l₁✝; α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ List.allb test l₁✝ = trueα:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ List.allb (fun x => !test x) l₂✝ = true α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ List.allb test l₁✝ = true All goals completed! 🐙 α:Typetest:α → Booll:List αl₁:List αl₂:List αx✝:αl₁✝:List αl₂✝:List αl₃✝:List αh':Merge l₁✝ l₂✝ l₃✝ih:List.allb test l₁✝ = true → List.allb (fun x => !test x) l₂✝ = true → filter test l₃✝ = l₁✝h₁:List.allb test l₁✝ = truehtest:test x✝ = falseh₂:List.allb (fun x => !test x) l₂✝ = true⊢ List.allb (fun x => !test x) l₂✝ = true All goals completed! 🐙
Exercise★★★★★(filter_challenge_2) (Advanced, Optional)

A different way to characterize the behavior of filter goes like this: Among all subsequences of l with the property that test evaluates to true on all their members, filter test l is the longest. Formalize this claim and prove it.

namespace FilterChallenge open LePlayground /- Here's a polymorphic version of `Subseq` we've seen above. -/ inductive Subseq {α : Type} : List α → List α → Prop where | nil {l : List α} : Subseq [] l | take {x : α} {l₁ l₂ : List α} (h : Subseq l₁ l₂) : Subseq (x :: l₁) (x :: l₂) | skip {x : α} {l₁ l₂ : List α} (h : Subseq l₁ l₂) : Subseq l₁ (x :: l₂) /- A few lemmas about subseq. -/ namespace Subseq theorem drop_l {α : Type} {x : α} {l₁ l₂ : List α} (h : Subseq (x :: l₁) l₂) : Subseq l₁ l₂ := α:Typex:αl₁:List αl₂:List αh:Subseq (x :: l₁) l₂⊢ Subseq l₁ l₂ induction l₂ generalizing l₁ with α:Typex:αl₁:List αh:Subseq (x :: l₁) []⊢ Subseq l₁ [] All goals completed! 🐙 α:Typex:αhead✝:αtail✝:List αih:∀ {l₁ : List α}, Subseq (x :: l₁) tail✝ → Subseq l₁ tail✝l₁:List αh:Subseq (x :: l₁) (head✝ :: tail✝)⊢ Subseq l₁ (head✝ :: tail✝) inversion h with | take hs => α:Typex:αtail✝:List αih:∀ {l₁ : List α}, Subseq (x :: l₁) tail✝ → Subseq l₁ tail✝l₁:List αhs:Subseq l₁ tail✝⊢ Subseq l₁ tail✝; All goals completed! 🐙 | skip hs => α:Typex:αhead✝:αtail✝:List αih:∀ {l₁ : List α}, Subseq (x :: l₁) tail✝ → Subseq l₁ tail✝l₁:List αhs:Subseq (x :: l₁) tail✝⊢ Subseq l₁ tail✝; α:Typex:αhead✝:αtail✝:List αih:∀ {l₁ : List α}, Subseq (x :: l₁) tail✝ → Subseq l₁ tail✝l₁:List αhs:Subseq (x :: l₁) tail✝⊢ Subseq (x :: l₁) tail✝; All goals completed! 🐙 theorem drop {α : Type} {x : α} {l₁ l₂ : List α} (h : Subseq (x :: l₁) (x :: l₂)) : Subseq l₁ l₂ := α:Typex:αl₁:List αl₂:List αh:Subseq (x :: l₁) (x :: l₂)⊢ Subseq l₁ l₂ inversion h with | take => All goals completed! 🐙 | skip => α:Typex:αl₁:List αl₂:List αh✝:Subseq (x :: l₁) l₂⊢ Subseq (?skip.x :: l₁) l₂α:Typex:αl₁:List αl₂:List αh✝:Subseq (x :: l₁) l₂⊢ α; All goals completed! 🐙 end Subseq /-- A list is _maximal_ with property `P` if it has the property, and every other list with the property is at most as long as it is. -/ def Maximal {α : Type} (maxList : List α) (P : List α → Prop) : Prop := P maxList ∧ ∀ (l : List α), P l → l.length ≤ maxList.length /-- A "good subsequence" for a given list `l` and a `test` is a subsequence of `l` all of whose members evaluate to `true` under the `test`. -/ def GoodSubseq {α : Type} (test : α → Bool) (l lsub : List α) := Subseq lsub l ∧ lsub.allb test /-- Good subsequences can be extended with good elements. -/ theorem GoodSubseq.extend {α : Type} {x : α} {l lsub : List α} {test : α → Bool} (hx : test x = true) (h : GoodSubseq test l lsub) : GoodSubseq test (x :: l) (x :: lsub) := α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = trueh:GoodSubseq test l lsub⊢ GoodSubseq test (x :: l) (x :: lsub) α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ GoodSubseq test (x :: l) (x :: lsub) α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ Subseq (x :: lsub) (x :: l)α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ List.allb test (x :: lsub) = true α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ Subseq (x :: lsub) (x :: l) α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ Subseq lsub l; All goals completed! 🐙 α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ List.allb test (x :: lsub) = true α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ test x = true ∧ List.allb test lsub = true α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ test x = trueα:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ List.allb test lsub = true α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ test x = true All goals completed! 🐙 α:Typex:αl:List αlsub:List αtest:α → Boolhx:test x = truehsub:Subseq lsub lhall:List.allb test lsub = true⊢ List.allb test lsub = true All goals completed! 🐙 /-- If `maxList` is a maximal good subsequence of `x :: l` and `x` is not good, then `maxList` is also a maximal good subsequence of `l`. -/ theorem maximal_strengthening {α : Type} {x : α} {maxList l : List α} {test : α → Bool} (hx : test x = false) (h : Maximal maxList (GoodSubseq test (x :: l))) : Maximal maxList (GoodSubseq test l) := α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falseh:Maximal maxList (GoodSubseq test (x :: l))⊢ Maximal maxList (GoodSubseq test l) α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ Maximal maxList (GoodSubseq test l) α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ GoodSubseq test l maxListα:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ maxList.length α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ Subseq maxList lα:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ List.allb test maxList = trueα:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ maxList.length α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ Subseq maxList l inversion hsub with | nil => All goals completed! 🐙 | take l₁ hsub => α:Typex:αl:List αtest:α → Boolhx:test x = falsel₁:List αhlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ (x :: l₁).lengthhall:test x = true ∧ List.allb test l₁ = truehsub:Subseq l₁ l⊢ Subseq (x :: l₁) l α:Typex:αl:List αtest:α → Boolhx:test x = falsel₁:List αhlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ (x :: l₁).lengthhsub:Subseq l₁ lht:test x = trueright✝:List.allb test l₁ = true⊢ Subseq (x :: l₁) l α:Typex:αl:List αtest:α → Boolhx:test x = falsel₁:List αhlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ (x :: l₁).lengthhsub:Subseq l₁ lht:false = trueright✝:List.allb test l₁ = true⊢ Subseq (x :: l₁) l; All goals completed! 🐙 | skip => All goals completed! 🐙 α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ List.allb test maxList = true All goals completed! 🐙 α:Typex:αmaxList:List αl:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = true⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ maxList.length α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ l.length ≤ maxList.length; α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ GoodSubseq test (x :: l✝) l; α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ Subseq l (x :: l✝)α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ List.allb test l = true α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ Subseq l (x :: l✝) α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ Subseq l l✝; All goals completed! 🐙 α:Typex:αmaxList:List αl✝:List αtest:α → Boolhx:test x = falsehlen:∀ (l_1 : List α), GoodSubseq test (x :: l) l_1 → l_1.length ≤ maxList.lengthhsub:Subseq maxList (x :: l)hall:List.allb test maxList = truel:List αhsub':Subseq l l✝hall':List.allb test l = true⊢ List.allb test l = true All goals completed! 🐙 /- Some easy lemmas about filter: its result is a good subsequence of the original list. -/ theorem filter_subseq {α : Type} (l : List α) (test : α → Bool) : Subseq (filter test l) l := α:Typel:List αtest:α → Bool⊢ Subseq (filter test l) l induction l with α:Typetest:α → Bool⊢ Subseq (filter test []) [] α:Typetest:α → Bool⊢ Subseq [] []; All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xs⊢ Subseq (filter test (x :: xs)) (x :: xs) α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = false⊢ Subseq (filter test (x :: xs)) (x :: xs)α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = true⊢ Subseq (filter test (x :: xs)) (x :: xs) α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = false⊢ Subseq (filter test (x :: xs)) (x :: xs) α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = false⊢ Subseq (filter test xs) (x :: xs) α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = false⊢ Subseq (filter test xs) xs; All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = true⊢ Subseq (filter test (x :: xs)) (x :: xs) α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = true⊢ Subseq (x :: filter test xs) (x :: xs) α:Typetest:α → Boolx:αxs:List αih:Subseq (filter test xs) xsh:test x = true⊢ Subseq (filter test xs) xs; All goals completed! 🐙 theorem filter_all {α : Type} (l : List α) (test : α → Bool) : (filter test l).allb test := α:Typel:List αtest:α → Bool⊢ List.allb test (filter test l) = true induction l with α:Typetest:α → Bool⊢ List.allb test (filter test []) = true All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = true⊢ List.allb test (filter test (x :: xs)) = true α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = false⊢ List.allb test (filter test (x :: xs)) = trueα:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ List.allb test (filter test (x :: xs)) = true α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = false⊢ List.allb test (filter test (x :: xs)) = true α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = false⊢ List.allb test (filter test xs) = true; All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ List.allb test (filter test (x :: xs)) = true α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ test x = true ∧ List.allb test (filter test xs) = true α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ test x = trueα:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ List.allb test (filter test xs) = true α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ test x = true All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:List.allb test (filter test xs) = trueh:test x = true⊢ List.allb test (filter test xs) = true All goals completed! 🐙 /- And now for the main theorem: `lsub` is a maximal good subsequence of `l` if and only if `filter test l = lsub` -/ theorem filter_spec2 {α : Type} (l lsub : List α) (test : α → Bool) : Maximal lsub (GoodSubseq test l) ↔ filter test l = lsub := α:Typel:List αlsub:List αtest:α → Bool⊢ Maximal lsub (GoodSubseq test l) ↔ filter test l = lsub α:Typel:List αlsub:List αtest:α → Bool⊢ Maximal lsub (GoodSubseq test l) → filter test l = lsubα:Typel:List αlsub:List αtest:α → Bool⊢ filter test l = lsub → Maximal lsub (GoodSubseq test l) α:Typel:List αlsub:List αtest:α → Bool⊢ Maximal lsub (GoodSubseq test l) → filter test l = lsub induction l generalizing lsub with α:Typetest:α → Boollsub:List α⊢ Maximal lsub (GoodSubseq test []) → filter test [] = lsub α:Typetest:α → Boollsub:List αhsub:Subseq lsub []hall:List.allb test lsub = truehlen:∀ (l : List α), GoodSubseq test [] l → l.length ≤ lsub.length⊢ filter test [] = lsub α:Typetest:α → Boolhall:List.allb test [] = truehlen:∀ (l : List α), GoodSubseq test [] l → l.length ≤ [].length⊢ filter test [] = [] All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List α⊢ Maximal lsub (GoodSubseq test (x :: xs)) → filter test (x :: xs) = lsub cases htest : test x with α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = false⊢ Maximal lsub (GoodSubseq test (x :: xs)) → filter test (x :: xs) = lsub α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = false⊢ Maximal lsub (GoodSubseq test (x :: xs)) → filter test xs = lsub α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = false⊢ Maximal lsub (GoodSubseq test (x :: xs)) → filter test xs = lsub α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = falsehmax:Maximal lsub (GoodSubseq test (x :: xs))⊢ filter test xs = lsub; α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = falsehmax:Maximal lsub (GoodSubseq test (x :: xs))⊢ Maximal lsub (GoodSubseq test xs) All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = true⊢ Maximal lsub (GoodSubseq test (x :: xs)) → filter test (x :: xs) = lsub α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsublsub:List αhtest:test x = truehsub:Subseq lsub (x :: xs)hall:List.allb test lsub = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ lsub.length⊢ filter test (x :: xs) = lsub /- in this case, `lsub` must begin with `x`, since otherwise it wouldn't be maximal. -/ cases lsub with α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truehsub:Subseq [] (x :: xs)hall:List.allb test [] = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ [].length⊢ filter test (x :: xs) = [] -- lsub = [] (impossible: contradicts maximality of lsub) α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truehsub:Subseq [] (x :: xs)hall:List.allb test [] = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ [].lengthcontra:[x].length ≤ [].length⊢ filter test (x :: xs) = [] All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truex':αxs':List αhsub:Subseq (x' :: xs') (x :: xs)hall:List.allb test (x' :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x' :: xs').length⊢ filter test (x :: xs) = x' :: xs' α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truex':αxs':List αhsub:Subseq (x' :: xs') (x :: xs)hall:List.allb test (x' :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x' :: xs').lengthheq:x = x'⊢ filter test (x :: xs) = x' :: xs' α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ filter test (x :: xs) = x :: xs'; α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ x :: filter test xs = x :: xs' α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ filter test xs = xs'; α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ Maximal xs' (GoodSubseq test xs); α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ GoodSubseq test xs xs'α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ ∀ (l : List α), GoodSubseq test xs l → l.length ≤ xs'.length; α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ Subseq xs' xsα:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ List.allb test xs' = trueα:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ ∀ (l : List α), GoodSubseq test xs l → l.length ≤ xs'.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ Subseq xs' xs All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ List.allb test xs' = true α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:test x = true ∧ List.allb test xs' = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ List.allb test xs' = true α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').lengthleft✝:test x = trueright✝:List.allb test xs' = true⊢ List.allb test xs' = true; All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').length⊢ ∀ (l : List α), GoodSubseq test xs l → l.length ≤ xs'.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ (x :: xs').lengthl':List αhgood:GoodSubseq test xs l'⊢ l'.length ≤ xs'.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ xs'.length + 1l':List αhgood:GoodSubseq test xs l'⊢ l'.length ≤ xs'.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ xs'.length + 1l':List αhgood:GoodSubseq test xs l'⊢ l'.length + 1 ≤ xs'.length + 1 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), Maximal lsub (GoodSubseq test xs) → filter test xs = lsubhtest:test x = truexs':List αhsub:Subseq (x :: xs') (x :: xs)hall:List.allb test (x :: xs') = truehlen:∀ (l : List α), GoodSubseq test (x :: xs) l → l.length ≤ xs'.length + 1l':List αhgood:GoodSubseq test xs l'⊢ GoodSubseq test (x :: xs) (x :: l') All goals completed! 🐙 α:Typel:List αlsub:List αtest:α → Bool⊢ filter test l = lsub → Maximal lsub (GoodSubseq test l) α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ Maximal lsub (GoodSubseq test l) α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ GoodSubseq test l lsubα:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ lsub.length; α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ GoodSubseq test l (filter test l)α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ lsub.length α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ Subseq (filter test l) lα:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ List.allb test (filter test l) = trueα:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ lsub.length α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ Subseq (filter test l) l All goals completed! 🐙 α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ List.allb test (filter test l) = true All goals completed! 🐙 α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsub⊢ ∀ (l_1 : List α), GoodSubseq test l l_1 → l_1.length ≤ lsub.length α:Typel:List αlsub:List αtest:α → Boolhfilter:filter test l = lsubl':List αhsub:Subseq l' lhall:List.allb test l' = true⊢ l'.length ≤ lsub.length induction l generalizing l' lsub with α:Typetest:α → Boollsub:List αhfilter:filter test [] = lsubl':List αhsub:Subseq l' []hall:List.allb test l' = true⊢ l'.length ≤ lsub.length α:Typetest:α → Boollsub:List αhfilter:filter test [] = lsubhall:List.allb test [] = true⊢ [].length ≤ lsub.length; α:Typetest:α → Boollsub:List αhfilter:filter test [] = lsubhall:List.allb test [] = true⊢ 0 ≤ lsub.length All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test (x :: xs) = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = true⊢ l'.length ≤ lsub.length cases htest : test x with α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test (x :: xs) = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = false⊢ l'.length ≤ lsub.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test xs = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = false⊢ l'.length ≤ lsub.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test xs = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = false⊢ l'.length ≤ lsub.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test xs = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = false⊢ Subseq l' xs inversion hsub with | nil => All goals completed! 🐙 | take l hsub => α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test xs = lsubhtest:test x = falsel:List αhall:test x = true ∧ List.allb test l = truehsub:Subseq l xs⊢ Subseq (x :: l) xs α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test xs = lsubhtest:test x = falsel:List αhsub:Subseq l xsht:test x = trueright✝:List.allb test l = true⊢ Subseq (x :: l) xs α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test xs = lsubhtest:true = falsel:List αhsub:Subseq l xsht:test x = trueright✝:List.allb test l = true⊢ Subseq (x :: l) xs All goals completed! 🐙 | skip hsub => All goals completed! 🐙 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:filter test (x :: xs) = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = true⊢ l'.length ≤ lsub.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = true⊢ l'.length ≤ lsub.length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubl':List αhsub:Subseq l' (x :: xs)hall:List.allb test l' = truehtest:test x = true⊢ l'.length ≤ (filter test xs).length + 1 inversion hsub with | nil => α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubhtest:test x = truehall:List.allb test [] = true⊢ 0 ≤ (filter test xs).length + 1; All goals completed! 🐙 | take l hsub => α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubhtest:test x = truel:List αhall:List.allb test (x :: l) = truehsub:Subseq l xs⊢ l.length + 1 ≤ (filter test xs).length + 1 α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubhtest:test x = truel:List αhall:List.allb test (x :: l) = truehsub:Subseq l xs⊢ l.length ≤ (filter test xs).length α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubhtest:test x = truel:List αhall:List.allb test (x :: l) = truehsub:Subseq l xs⊢ List.allb test l = true α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubhtest:test x = truel:List αhall:test x = true ∧ List.allb test l = truehsub:Subseq l xs⊢ List.allb test l = true α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubhtest:test x = truel:List αhsub:Subseq l xsleft✝:test x = trueh:List.allb test l = true⊢ List.allb test l = true All goals completed! 🐙 | skip hsub => α:Typetest:α → Boolx:αxs:List αih:∀ (lsub : List α), filter test xs = lsub → ∀ (l' : List α), Subseq l' xs → List.allb test l' = true → l'.length ≤ lsub.lengthlsub:List αhfilter:x :: filter test xs = lsubl':List αhall:List.allb test l' = truehtest:test x = truehsub:Subseq l' xs⊢ l'.length ≤ (filter test xs).length All goals completed! 🐙 end FilterChallenge
Exercise★★★★(palindromes) (Optional)

A palindrome is a sequence that reads the same backwards as forwards.

  • Define an inductive proposition Pal on List α that captures what it means to be a palindrome. (Hint: You'll need three cases.)

  • Prove pal_append_reverse, which states that

∀ l, Pal (l ++ l.reverse).
  • Prove pal_reverse, which states that

∀ l, Pal l → l = l.reverse.

For extra credit, try proving the same theorems with an alternate definition with a single constructor of this type:

∀ l, l = l.reverse → Pal l
inductive Pal {α : Type} : List α → Prop where | nil : Pal [] | singleton {x : α} : Pal [x] | cons_snoc {x : α} {l : List α} (h : Pal l) : Pal (x :: (l ++ [x])) example : Pal ([] : List Nat) := ⊢ Pal [] suggested! All goals completed! 🐙 example : Pal [1] := ⊢ Pal [1] suggested! All goals completed! 🐙 example : Pal [1, 2, 1] := ⊢ Pal [1, 2, 1] suggested! ⊢ Pal [2] All goals completed! 🐙 example : Pal [1, 2, 3, 2, 1] := ⊢ Pal [1, 2, 3, 2, 1] suggested! ⊢ Pal [2, 3, 2] ⊢ Pal [3] All goals completed! 🐙 theorem pal_append_reverse (α : Type) (l : List α) : Pal (l ++ l.reverse) := α:Typel:List α⊢ Pal (l ++ l.reverse) solution! induction l with α:Type⊢ Pal ([] ++ [].reverse) α:Type⊢ Pal []; All goals completed! 🐙 α:Typex:αxs:List αih:Pal (xs ++ xs.reverse)⊢ Pal (x :: xs ++ (x :: xs).reverse) α:Typex:αxs:List αih:Pal (xs ++ xs.reverse)⊢ Pal (x :: (xs ++ xs.reverse ++ [x])) α:Typex:αxs:List αih:Pal (xs ++ xs.reverse)⊢ Pal (xs ++ xs.reverse); All goals completed! 🐙 theorem pal_reverse {α : Type} {l : List α} (hp : Pal l) : l = l.reverse := α:Typel:List αhp:Pal l⊢ l = l.reverse solution! induction hp with α:Typel:List α⊢ [] = [].reverse All goals completed! 🐙 α:Typel:List αx✝:α⊢ [x✝] = [x✝].reverse All goals completed! 🐙 α:Typel:List αx✝:αl✝:List αh:Pal l✝ih:l✝ = l✝.reverse⊢ x✝ :: (l✝ ++ [x✝]) = (x✝ :: (l✝ ++ [x✝])).reverse α:Typel:List αx✝:αl✝:List αh:Pal l✝ih:l✝ = l✝.reverse⊢ x✝ :: l✝ ++ [x✝] = [x✝].reverse ++ l✝ ++ [x✝] All goals completed! 🐙
Exercise★★★★(NoDup) (Advanced, Optional, Manually graded)

Use the ∈ property to define a proposition Disjoint l₁ l₂, which should be provable exactly when l₁ and l₂ are lists (with elements of type α) that have no elements in common.

def Disjoint {α : Type} (l₁ l₂ : List α) : Prop := solution!(∀ {x : α}, x ∈ l₁ → ¬ x ∈ l₂)

Next, use ∈ to define an inductive proposition NoDup l, which should be provable exactly when l is a list (with elements of type α) where every member is different from every other. For example, NoDup ([1, 2, 3, 4] : List Nat) and NoDup ([] : List Bool) should be provable, while NoDup ([1, 2, 1] : List Nat) and NoDup ([true, true] : List Bool) should not be.

inductive NoDup {α : Type} : List α → Prop where | nil : NoDup [] | cons {x : α} {l : List α} (hnin : ¬ x ∈ l) (h : NoDup l) : NoDup (x :: l)

Finally, state and prove one or more interesting theorems relating Disjoint, NoDup, and ++ (list append).

Here are some possible answers:

theorem NoDup.append {α : Type} {l₁ l₂: List α} (h₁ : NoDup l₁) (h₂ : NoDup l₂) (hdis : Disjoint l₁ l₂) : NoDup (l₁ ++ l₂) := α:Typel₁:List αl₂:List αh₁:NoDup l₁h₂:NoDup l₂hdis:Disjoint l₁ l₂⊢ NoDup (l₁ ++ l₂) induction l₁ generalizing l₂ with α:Typel₂:List αh₁:NoDup []h₂:NoDup l₂hdis:Disjoint [] l₂⊢ NoDup ([] ++ l₂) α:Typel₂:List αh₁:NoDup []h₂:NoDup l₂hdis:Disjoint [] l₂⊢ NoDup l₂; All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ NoDup (x :: xs ++ l₂) α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ ¬x ∈ xs.append l₂α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ NoDup (xs.append l₂) α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ ¬x ∈ xs.append l₂ α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂contra:x ∈ xs.append l₂⊢ False α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂contra:x ∈ xs ∨ x ∈ l₂⊢ False cases contra with α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂h✝:x ∈ xs⊢ False inversion h₁ with | _ hdup hin => α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₂:NoDup l₂hdis:Disjoint (x :: xs) l₂h✝:x ∈ xshdup:NoDup xshin:¬x ∈ xs⊢ x ∈ xs; All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂contra:x ∈ l₂⊢ False α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂contra:x ∈ l₂⊢ x ∈ x :: xs α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂contra:x ∈ l₂⊢ x = x ∨ x ∈ xs; α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂contra:x ∈ l₂⊢ x = x; All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ NoDup (xs.append l₂) α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ NoDup xsα:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ Disjoint xs l₂ α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ NoDup xs α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₂:NoDup l₂hdis:Disjoint (x :: xs) l₂h✝:NoDup xshnin✝:¬x ∈ xs⊢ NoDup xs; All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂⊢ Disjoint xs l₂ α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂x:αhin:x ∈ xs⊢ ¬x ∈ l₂ α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂x:αhin:x ∈ xs⊢ x ∈ x✝ :: xs; α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂x:αhin:x ∈ xs⊢ x = x✝ ∨ x ∈ xs α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup xs → NoDup l₂ → Disjoint xs l₂ → NoDup (xs ++ l₂)l₂:List αh₁:NoDup (x :: xs)h₂:NoDup l₂hdis:Disjoint (x :: xs) l₂x:αhin:x ∈ xs⊢ x ∈ xs; All goals completed! 🐙 theorem NoDup.isDisjoint {α : Type} {l₁ l₂: List α} (h : NoDup (l₁ ++ l₂)) : Disjoint l₁ l₂ := α:Typel₁:List αl₂:List αh:NoDup (l₁ ++ l₂)⊢ Disjoint l₁ l₂ α:Typel₁:List αl₂:List αh:NoDup (l₁ ++ l₂)x:αhin:x ∈ l₁contra:x ∈ l₂⊢ False induction l₁ generalizing l₂ x with α:Typel₂:List αh:NoDup ([] ++ l₂)x:αhin:x ∈ []contra:x ∈ l₂⊢ False α:Typel₂:List αh:NoDup ([] ++ l₂)x:αhin:Falsecontra:x ∈ l₂⊢ False; All goals completed! 🐙 α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αh:NoDup (x✝ :: xs ++ l₂)x:αhin:x ∈ x✝ :: xscontra:x ∈ l₂⊢ False α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αh:NoDup (x✝ :: xs ++ l₂)x:αhin:x = x✝ ∨ x ∈ xscontra:x ∈ l₂⊢ False inversion h with | cons hdup hnin => cases hin with α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αx:αcontra:x ∈ l₂hdup:NoDup (xs.append l₂)hnin:¬x✝ ∈ xs.append l₂hin:x = x✝⊢ False α:Typexs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αx:αcontra:x ∈ l₂hdup:NoDup (xs.append l₂)hnin:¬x ∈ xs.append l₂⊢ False; α:Typexs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αx:αcontra:x ∈ l₂hdup:NoDup (xs.append l₂)hnin:¬x ∈ xs.append l₂⊢ x ∈ xs.append l₂ α:Typexs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αx:αcontra:x ∈ l₂hdup:NoDup (xs.append l₂)hnin:¬x ∈ xs.append l₂⊢ x ∈ xs ∨ x ∈ l₂; α:Typexs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αx:αcontra:x ∈ l₂hdup:NoDup (xs.append l₂)hnin:¬x ∈ xs.append l₂⊢ x ∈ l₂; All goals completed! 🐙 α:Typex✝:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → ∀ {x : α}, x ∈ xs → x ∈ l₂ → Falsel₂:List αx:αcontra:x ∈ l₂hdup:NoDup (xs.append l₂)hnin:¬x✝ ∈ xs.append l₂hin:x ∈ xs⊢ False All goals completed! 🐙 /- We can also show the following results about `NoDup` and `++` by themselves -/ theorem NoDup.left {α : Type} {l₁ l₂: List α} (hdup : NoDup (l₁ ++ l₂)) : NoDup l₁ := α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ NoDup l₁ induction l₁ generalizing l₂ with α:Typel₂:List αhdup:NoDup ([] ++ l₂)⊢ NoDup [] All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup:NoDup (x :: xs ++ l₂)⊢ NoDup (x :: xs) inversion hdup with | cons hdup' hin => α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂⊢ ¬x ∈ xsα:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂⊢ NoDup xs α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂⊢ ¬x ∈ xs α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂contra:x ∈ xs⊢ False; α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂contra:x ∈ xs⊢ x ∈ xs.append l₂ α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂contra:x ∈ xs⊢ x ∈ xs ∨ x ∈ l₂; α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂contra:x ∈ xs⊢ x ∈ xs; All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup xsl₂:List αhdup':NoDup (xs.append l₂)hin:¬x ∈ xs.append l₂⊢ NoDup xs All goals completed! 🐙 theorem NoDup.right {α : Type} {l₁ l₂ : List α} (h : NoDup (l₁ ++ l₂)) : NoDup l₂ := α:Typel₁:List αl₂:List αh:NoDup (l₁ ++ l₂)⊢ NoDup l₂ induction l₁ generalizing l₂ with α:Typel₂:List αh:NoDup ([] ++ l₂)⊢ NoDup l₂ α:Typel₂:List αh:NoDup l₂⊢ NoDup l₂; All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup l₂l₂:List αh:NoDup (x :: xs ++ l₂)⊢ NoDup l₂ α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup l₂l₂:List αh✝:NoDup (xs.append l₂)hnin✝:¬x ∈ xs.append l₂⊢ NoDup l₂ α:Typex:αxs:List αih:∀ {l₂ : List α}, NoDup (xs ++ l₂) → NoDup l₂l₂:List αh✝:NoDup (xs.append l₂)hnin✝:¬x ∈ xs.append l₂⊢ NoDup (xs ++ l₂); All goals completed! 🐙 /- This theorem combines the various lemmas to give a complete characterization -/ theorem NoDup.disjoint_app {α : Type} {l₁ l₂ : List α} : NoDup (l₁ ++ l₂) ↔ (NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂) := α:Typel₁:List αl₂:List α⊢ NoDup (l₁ ++ l₂) ↔ NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂ α:Typel₁:List αl₂:List α⊢ NoDup (l₁ ++ l₂) → NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂α:Typel₁:List αl₂:List α⊢ NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂ → NoDup (l₁ ++ l₂) α:Typel₁:List αl₂:List α⊢ NoDup (l₁ ++ l₂) → NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂ α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂ α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ NoDup l₁α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ NoDup l₂ ∧ Disjoint l₁ l₂; α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ NoDup l₂ ∧ Disjoint l₁ l₂ α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ NoDup l₂α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ Disjoint l₁ l₂; α:Typel₁:List αl₂:List αhdup:NoDup (l₁ ++ l₂)⊢ Disjoint l₁ l₂ All goals completed! 🐙 α:Typel₁:List αl₂:List α⊢ NoDup l₁ ∧ NoDup l₂ ∧ Disjoint l₁ l₂ → NoDup (l₁ ++ l₂) α:Typel₁:List αl₂:List αh₁:NoDup l₁h₂:NoDup l₂h₃:Disjoint l₁ l₂⊢ NoDup (l₁ ++ l₂) All goals completed! 🐙
Exercise★★★★★(pigeonhole_principle) (Advanced, Optional)

The pigeonhole principle states a basic fact about counting: if we distribute more than n items into n pigeonholes, some pigeonhole must contain at least two items. As often happens, this apparently trivial fact about numbers requires nontrivial machinery to prove, but we now have enough...

First prove an easy and useful lemma.

theorem List.mem_split {α : Type} {x : α} {l : List α} (hin : x ∈ l) : ∃ l₁ l₂, l = l₁ ++ x :: l₂ := α:Typex:αl:List αhin:x ∈ l⊢ ∃ l₁ l₂, l = l₁ ++ x :: l₂ solution! -- The exact lemma is called `List.append_of_mem` in Lean's core library induction l generalizing x with α:Typex:αhin:x ∈ []⊢ ∃ l₁ l₂, [] = l₁ ++ x :: l₂ α:Typex:αhin:False⊢ ∃ l₁ l₂, [] = l₁ ++ x :: l₂; All goals completed! 🐙 α:Typex':αxs':List αih:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:αhin:x ∈ x' :: xs'⊢ ∃ l₁ l₂, x' :: xs' = l₁ ++ x :: l₂ α:Typex':αxs':List αih:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:αhin:x = x' ∨ x ∈ xs'⊢ ∃ l₁ l₂, x' :: xs' = l₁ ++ x :: l₂ cases hin with α:Typex':αxs':List αih:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:αhin:x = x'⊢ ∃ l₁ l₂, x' :: xs' = l₁ ++ x :: l₂ α:Typexs':List αih:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:α⊢ ∃ l₁ l₂, x :: xs' = l₁ ++ x :: l₂ α:Typexs':List αih:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:α⊢ ∃ l₂, x :: xs' = [] ++ x :: l₂ All goals completed! 🐙 α:Typex':αxs':List αih:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:αhin:x ∈ xs'⊢ ∃ l₁ l₂, x' :: xs' = l₁ ++ x :: l₂ α:Typex':αxs':List αih✝:∀ {x : α}, x ∈ xs' → ∃ l₁ l₂, xs' = l₁ ++ x :: l₂x:αhin:x ∈ xs'l₁':List αl₂':List αih:xs' = l₁' ++ x :: l₂'⊢ ∃ l₁ l₂, x' :: xs' = l₁ ++ x :: l₂ α:Typex':αx:αl₁':List αl₂':List αih:∀ {x_1 : α}, x_1 ∈ l₁' ++ x :: l₂' → ∃ l₁ l₂, l₁' ++ x :: l₂' = l₁ ++ x_1 :: l₂hin:x ∈ l₁' ++ x :: l₂'⊢ ∃ l₁ l₂, x' :: (l₁' ++ x :: l₂') = l₁ ++ x :: l₂ α:Typex':αx:αl₁':List αl₂':List αih:∀ {x_1 : α}, x_1 ∈ l₁' ++ x :: l₂' → ∃ l₁ l₂, l₁' ++ x :: l₂' = l₁ ++ x_1 :: l₂hin:x ∈ l₁' ++ x :: l₂'⊢ ∃ l₂, x' :: (l₁' ++ x :: l₂') = x' :: l₁' ++ x :: l₂ All goals completed! 🐙

Now define a property Repeats such that Repeats l asserts that l contains at least one repeated element.

inductive Repeats {α : Type} : List α → Prop where | head {x : α} {l : List α} (h : x ∈ l) : Repeats (x :: l) | tail {x : α} {l : List α} (h : Repeats l) : Repeats (x :: l)

Now, here's a way to formalize the pigeonhole principle. Suppose list l₂ represents a list of pigeonhole labels, and list l₁ represents the labels assigned to a list of items. If there are more items than labels, at least two items must have the same label — i.e., list l₁ must contain repeats.

This proof is much easier if you use the excluded middle to show that ∈ is decidable, i.e., ∀ x l, (x ∈ l) ∨ ¬ (x ∈ l). Remember the by_cases tactic from Logic!

Note to developers

HIDE: APT21: Apparently, this is really quite hard; even the strongest students couldn't do it this year.

Note to developers (Yipeng Liu @berberman)

I reworked the proof and felt the list membership reasoning is too distracting. Maybe move to the Automation chapter for simp.

open LePlayground in theorem pigeonhole_principle {α : Type} {l₁ l₂ : List α} (hin : ∀ x, x ∈ l₁ → x ∈ l₂) (hlen : l₂.length < l₁.length) : Repeats l₁ := α:Typel₁:List αl₂:List αhin:∀ (x : α), x ∈ l₁ → x ∈ l₂hlen:l₂.length < l₁.length⊢ Repeats l₁ solution! induction l₁ generalizing l₂ with α:Typel₂:List αhin:∀ (x : α), x ∈ [] → x ∈ l₂hlen:l₂.length < [].length⊢ Repeats [] α:Typel₂:List αhin:∀ (x : α), x ∈ [] → x ∈ l₂hlen:l₂.length < 0⊢ Repeats [] α:Typel₂:List αhin:∀ (x : α), x ∈ [] → x ∈ l₂hlen:False⊢ Repeats [] All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).length⊢ Repeats (x :: xs) α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).lengthh:x ∈ xs⊢ Repeats (x :: xs)α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).lengthh:¬x ∈ xs⊢ Repeats (x :: xs) α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).lengthh:x ∈ xs⊢ Repeats (x :: xs) All goals completed! 🐙 α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).lengthh:¬x ∈ xs⊢ Repeats (x :: xs) α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).lengthh:¬x ∈ xs⊢ Repeats xs α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂hlen:l₂.length < (x :: xs).lengthh:¬x ∈ xsh₂:x ∈ l₂⊢ Repeats xs α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsh:¬x ∈ xsl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂a ++ x :: l₂bhlen:(l₂a ++ x :: l₂b).length < (x :: xs).lengthh₂:x ∈ l₂a ++ x :: l₂b⊢ Repeats xs α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsh:¬x ∈ xsl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂a ++ x :: l₂bhlen:(l₂a ++ x :: l₂b).length < (x :: xs).lengthh₂:x ∈ l₂a ++ x :: l₂bhin₂:∀ (y : α), y ∈ xs → y ∈ l₂a ++ l₂b⊢ Repeats xs α:Typex:αxs:List αih:∀ {l₂ : List α}, (∀ (x : α), x ∈ xs → x ∈ l₂) → l₂.length < xs.length → Repeats xsh:¬x ∈ xsl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: xs → x_1 ∈ l₂a ++ x :: l₂bhlen:(l₂a ++ x :: l₂b).length < (x :: xs).lengthh₂:x ∈ l₂a ++ x :: l₂bhin₂:∀ (y : α), y ∈ xs → y ∈ l₂a ++ l₂bhlen₂:(l₂a ++ l₂b).length < xs.length⊢ Repeats xs All goals completed! 🐙

Here is a different way to prove the pigeonhole principle, adapted from proofs by Daniel Schepler and N. Raghavendra. Unlike the proof above, it never needs to decide whether an element belongs to a list, so it does not rely on the law of excluded middle.

-- Claude-generated solution theorem repeats_insert {α : Type} (l₁ l₂ : List α) (x : α) (h : x ∈ l₁ ++ l₂) : Repeats (l₁ ++ x :: l₂) := α:Typel₁:List αl₂:List αx:αh:x ∈ l₁ ++ l₂⊢ Repeats (l₁ ++ x :: l₂) induction l₁ generalizing l₂ with α:Typex:αl₂:List αh:x ∈ [] ++ l₂⊢ Repeats ([] ++ x :: l₂) All goals completed! 🐙 α:Typex:αy:αys:List αih:∀ (l₂ : List α), x ∈ ys ++ l₂ → Repeats (ys ++ x :: l₂)l₂:List αh:x ∈ y :: ys ++ l₂⊢ Repeats (y :: ys ++ x :: l₂) α:Typex:αy:αys:List αih:∀ (l₂ : List α), x ∈ ys ++ l₂ → Repeats (ys ++ x :: l₂)l₂:List αh:x = y ∨ x ∈ ys ++ l₂⊢ Repeats (y :: (ys ++ x :: l₂)) α:Typex:αys:List αih:∀ (l₂ : List α), x ∈ ys ++ l₂ → Repeats (ys ++ x :: l₂)l₂:List α⊢ Repeats (x :: (ys ++ x :: l₂))α:Typex:αy:αys:List αih:∀ (l₂ : List α), x ∈ ys ++ l₂ → Repeats (ys ++ x :: l₂)l₂:List αh:x ∈ ys ++ l₂⊢ Repeats (y :: (ys ++ x :: l₂)) α:Typex:αys:List αih:∀ (l₂ : List α), x ∈ ys ++ l₂ → Repeats (ys ++ x :: l₂)l₂:List α⊢ Repeats (x :: (ys ++ x :: l₂)) All goals completed! 🐙 α:Typex:αy:αys:List αih:∀ (l₂ : List α), x ∈ ys ++ l₂ → Repeats (ys ++ x :: l₂)l₂:List αh:x ∈ ys ++ l₂⊢ Repeats (y :: (ys ++ x :: l₂)) All goals completed! 🐙 theorem pigeonhole_aux {α : Type} (l₁ : List α) : ∀ (u l₂ : List α), (∀ x, x ∈ l₁ → x ∈ u ++ l₂) → l₂.length < l₁.length → Repeats (u ++ l₁) := α:Typel₁:List α⊢ ∀ (u l₂ : List α), (∀ (x : α), x ∈ l₁ → x ∈ u ++ l₂) → l₂.length < l₁.length → Repeats (u ++ l₁) induction l₁ with α:Type⊢ ∀ (u l₂ : List α), (∀ (x : α), x ∈ [] → x ∈ u ++ l₂) → l₂.length < [].length → Repeats (u ++ []) α:Typeu:List αl₂:List αa✝:∀ (x : α), x ∈ [] → x ∈ u ++ l₂hlen:l₂.length < [].length⊢ Repeats (u ++ []); All goals completed! 🐙 α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)⊢ ∀ (u l₂ : List α), (∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂) → l₂.length < (x :: t).length → Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂hlen:l₂.length < (x :: t).length⊢ Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂hlen:l₂.length < (x :: t).lengthhx:x ∈ u ++ l₂⊢ Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂hlen:l₂.length < (x :: t).lengthhx:x ∈ u ++ l₂hxu:x ∈ u⊢ Repeats (u ++ x :: t)α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂hlen:l₂.length < (x :: t).lengthhx:x ∈ u ++ l₂hxl₂:x ∈ l₂⊢ Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂hlen:l₂.length < (x :: t).lengthhx:x ∈ u ++ l₂hxu:x ∈ u⊢ Repeats (u ++ x :: t) All goals completed! 🐙 α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ l₂hlen:l₂.length < (x :: t).lengthhx:x ∈ u ++ l₂hxl₂:x ∈ l₂⊢ Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂b⊢ Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ t⊢ Repeats (u ++ x :: t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ t⊢ Repeats (u ++ [x] ++ t) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ t⊢ ∀ (x_1 : α), x_1 ∈ t → x_1 ∈ u ++ [x] ++ (l₂a ++ l₂b)α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ t⊢ (l₂a ++ l₂b).length < t.length α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ t⊢ ∀ (x_1 : α), x_1 ∈ t → x_1 ∈ u ++ [x] ++ (l₂a ++ l₂b) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ t⊢ y ∈ u ++ [x] ++ (l₂a ++ l₂b) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thy':y ∈ u ++ (l₂a ++ x :: l₂b)⊢ y ∈ u ++ [x] ++ (l₂a ++ l₂b) α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thy':y ∈ u ∨ y ∈ l₂a ∨ y = x ∨ y ∈ l₂b⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂b α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyu:y ∈ u⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂bα:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyl:y ∈ l₂a⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂bα:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyx:y = x⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂bα:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyb:y ∈ l₂b⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂b α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyu:y ∈ u⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂b All goals completed! 🐙 α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyl:y ∈ l₂a⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂b All goals completed! 🐙 α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyx:y = x⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂b All goals completed! 🐙 α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ ty:αhy:y ∈ thyb:y ∈ l₂b⊢ (y ∈ u ∨ y = x) ∨ y ∈ l₂a ∨ y ∈ l₂b All goals completed! 🐙 α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hlen:(l₂a ++ x :: l₂b).length < (x :: t).lengthhx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ t⊢ (l₂a ++ l₂b).length < t.length α:Typex:αt:List αih:∀ (u l₂ : List α), (∀ (x : α), x ∈ t → x ∈ u ++ l₂) → l₂.length < t.length → Repeats (u ++ t)u:List αl₂a:List αl₂b:List αhin:∀ (x_1 : α), x_1 ∈ x :: t → x_1 ∈ u ++ (l₂a ++ x :: l₂b)hx:x ∈ u ++ (l₂a ++ x :: l₂b)hxl₂:x ∈ l₂a ++ x :: l₂bheq:u ++ x :: t = u ++ [x] ++ thlen:l₂a.length + (l₂b.length + 1) < t.length + 1⊢ l₂a.length + l₂b.length < t.length All goals completed! 🐙 theorem pigeonhole_principle' {α : Type} {l₁ l₂ : List α} (hin : ∀ x, x ∈ l₁ → x ∈ l₂) (hlen : l₂.length < l₁.length) : Repeats l₁ := pigeonhole_aux l₁ [] l₂ hin hlen
Source revision: 9cc9a7b, committed 2026-09-28 19:09 UTC