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 α)
Next, we look at what happens when we repeatedly apply collatzStep to
some given starting number. For example, collatzStep12 is 6, and
collatzStep6 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.
deffail to show termination forreaches1Inwith errorsfailed to infer structural recursion:Cannot use parameter n:failed to eliminate recursive applicationreaches1In(collatzStepn)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 goaln:Nat⊢ collatzStepn<nreaches1In(n:Nat):Nat:=bifn==1then0else1+reaches1In(collatzStepn)
fail to show termination forreaches1Inwith errorsfailed to infer structural recursion:Cannot use parameter n:failed to eliminate recursive applicationreaches1In(collatzStepn)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 goaln:Nat⊢ collatzStepn<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!
deffail to show termination forCollatzHoldsForwith errorsfailed to infer structural recursion:Cannot use parameter n:failed to eliminate recursive applicationCollatzHoldsFor(div2n)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 goalnx✝:Nat⊢ div2n<x✝CollatzHoldsFor(n:Nat):Prop:=matchnwith|0=>False|1=>True|_=>bifn.eventhenCollatzHoldsFor(div2n)elseCollatzHoldsFor((3*n)+1)
fail to show termination forCollatzHoldsForwith errorsfailed to infer structural recursion:Cannot use parameter n:failed to eliminate recursive applicationCollatzHoldsFor(div2n)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 goalnx✝:Nat⊢ div2n<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:
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):
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(div212) to CollatzHoldsFor6, etc.
The Collatz conjecture then states that the sequence beginning
from any positive number reaches 1:
defCollatz:=∀n:Nat,n≠0→CollatzHoldsForn
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!
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.
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.
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
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.
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
For instance, this enables an equivalent definition of the Collatz
conjecture. First we define a binary relation corresponding to
the "Collatz step function" collatzStep:
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:
This CollatzStepMulti relation defined in terms of
ReflTransGen allows for more interesting derivations than the
linear ones of the directly defined CollatzHoldsFor relation:
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:
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:
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):Typewhere|nil|cons(x:α)(l:Listα)
or (equivalently but more explicitly):
inductive`List` has already been declaredList(α:Type):Typewhere|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:
inductiveWrongEven(n:Nat):PropwhereMismatched inductive type parameter inWrongEven0The provided argument0is not definitionally equal to the expected parameternNote: The value of parameter `n` must be fixed throughout the inductive declaration. Consider making this parameter an index if it must vary.|zero:WrongEven0|succ_succ(h:WrongEvenn):WrongEven(.succ(.succn))
Mismatched inductive type parameter inWrongEven0The provided argument0is not definitionally equal to the expected parameternNote: 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":
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...
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.)
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.
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 eliminateEven
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.
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 ndirectly.
As a tool for such proofs, we can formalize the intuitive
characterization that we gave above for evidence of Even n,
using cases.
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.
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.
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:
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.
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?
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:Evenn):Nat.Evenn:=byn:Nath:Evenn⊢ n.Evenunsolved goalssucc_succn':Nath':Evenn'⊢ (n'+2).Eveninversionhwith|zero=>exists0All goals completed! 🐙-- The first case can be solved trivially.|succ_succn'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:
theoremEven.nat_even(n:Nat)(h:Evenn):Nat.Evenn:=byn:Nath:Evenn⊢ n.Eveninductionhwith|zero=>zeron:Nat⊢ Nat.Even0exists0All goals completed! 🐙-- (`0 = double 0` is closed by `exists`'s final `rfl`)|succ_succh'ih=>succ_succn:Natn✝:Nath':Evenn✝ih:n✝.Even⊢ (n✝+2).Evenlet⟨k,hk⟩:=ihsucc_succn:Natn✝:Nath':Evenn✝ih:n✝.Evenk:Nathk:n✝=k.double⊢ (n✝+2).Evenexistsk+1succ_succn:Natn✝:Nath':Evenn✝ih:n✝.Evenk:Nathk:n✝=k.double⊢ n✝+2=(k+1).double;rw[Nat.double_succ,succ_succn:Natn✝:Nath':Evenn✝ih:n✝.Evenk:Nathk:n✝=k.double⊢ n✝+2=k.double+2hksucc_succn:Natn✝:Nath':Evenn✝ih:n✝.Evenk:Nathk:n✝=k.double⊢ k.double+2=k.double+2]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.
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.
theoremEven.of_add_left(nm:Nat)(h:Even(n+m))(hn:Evenn):Evenm:=byn:Natm:Nath:Even(n+m)hn:Evenn⊢ Evenm/- Hint: There are two pieces of evidence you could attempt to induct upon
here. If one doesn't work, try the other. -/solution!inductionhngeneralizingmwith|zero=>zeron:Natm:Nath:Even(0+m)⊢ Evenmrw[Nat.zero_addzeron:Natm:Nath:Evenm⊢ Evenm]athzeron:Natm:Nath:Evenm⊢ Evenm;exacthAll goals completed! 🐙|succ_succh'ih=>succ_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath:Even(n✝+2+m)⊢ Evenmrw[Nat.add_comm,succ_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath:Even(m+(n✝+2))⊢ EvenmNat.add_succ,succ_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath:Even(m+(n✝+1)).succ⊢ EvenmNat.add_succ,succ_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath:Even(m+n✝).succ.succ⊢ EvenmNat.add_commsucc_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath:Even(n✝+m).succ.succ⊢ Evenm]athsucc_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath:Even(n✝+m).succ.succ⊢ Evenminversionhsucc_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath✝:Even(n✝+m)⊢ Evenm;applyihsucc_succn:Natn✝:Nath':Evenn✝ih:∀(m:Nat),Even(n✝+m)→Evenmm:Nath✝:Even(n✝+m)⊢ Even(n✝+m);assumptionAll 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?
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:
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:
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.
defDiagonal{α:Type}(r:α→α→Prop):=∀{xy},rxy→x=y
Now consider the following lemma about diagonal relations:
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:
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.
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.
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.
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.
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:
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.
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:
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,
Define an inductive proposition Subseq on ListNat 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).
inductiveSubseq:ListNat→ListNat→Propwhere|nil{l:ListNat}:Subseq[]l|take{x:Nat}{l₁l₂:ListNat}(h:Subseql₁l₂):Subseq(x::l₁)(x::l₂)|skip{x:Nat}{l₁l₂:ListNat}(h:Subseql₁l₂):Subseql₁(x::l₂)namespaceSubseqtheoremrefl(l:ListNat):Subseqll:=byl:ListNat⊢ Subseqllsolution!inductionlwith|nil=>nil⊢ Subseq[][]constructorAll goals completed! 🐙|consxxsih=>consx:Natxs:ListNatih:Subseqxsxs⊢ Subseq(x::xs)(x::xs)constructorconsx:Natxs:ListNatih:Subseqxsxs⊢ Subseqxsxs;assumptionAll goals completed! 🐙theoremappend(l₁l₂l₃:ListNat)(h:Subseql₁l₂):Subseql₁(l₂++l₃):=byl₁:ListNatl₂:ListNatl₃:ListNath:Subseql₁l₂⊢ Subseql₁(l₂++l₃)solution!inductionhwith|nil=>nill₁:ListNatl₂:ListNatl₃:ListNatl✝:ListNat⊢ Subseq[](l✝++l₃)constructorAll goals completed! 🐙|take=>takel₁:ListNatl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝h_ih✝:Subseql₁✝(l₂✝++l₃)⊢ Subseq(x✝::l₁✝)(x✝::l₂✝++l₃)constructortakel₁:ListNatl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝h_ih✝:Subseql₁✝(l₂✝++l₃)⊢ Subseql₁✝(l₂✝.appendl₃);assumptionAll goals completed! 🐙|skip=>skipl₁:ListNatl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝h_ih✝:Subseql₁✝(l₂✝++l₃)⊢ Subseql₁✝(x✝::l₂✝++l₃)constructorskipl₁:ListNatl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝h_ih✝:Subseql₁✝(l₂✝++l₃)⊢ Subseql₁✝(l₂✝.appendl₃);assumptionAll goals completed! 🐙theoremtrans(l₁l₂l₃:ListNat)(h12:Subseql₁l₂)(h23:Subseql₂l₃):Subseql₁l₃:=byl₁:ListNatl₂:ListNatl₃:ListNath12:Subseql₁l₂h23:Subseql₂l₃⊢ Subseql₁l₃/- Hint: be careful about what you are doing induction on and which
other things need to be generalized... -/solution!inductionh23generalizingl₁with|nil=>nill₂:ListNatl₃:ListNatl✝:ListNatl₁:ListNath12:Subseql₁[]⊢ Subseql₁l✝inversionh12nill₂:ListNatl₃:ListNatl✝:ListNat⊢ Subseq[]l✝;constructorAll goals completed! 🐙|take_ih=>takel₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath12:Subseql₁(x✝::l₁✝)⊢ Subseql₁(x✝::l₂✝)inversionh12nill₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝⊢ Subseq[](x✝::l₂✝)takel₂:ListNatl₃:ListNatx✝:Natl₁✝¹:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁✝:ListNath✝:Subseql₁✝l₁✝¹⊢ Subseq(x✝::l₁✝)(x✝::l₂✝)skipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath✝:Subseql₁l₁✝⊢ Subseql₁(x✝::l₂✝);constructortakel₂:ListNatl₃:ListNatx✝:Natl₁✝¹:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁✝:ListNath✝:Subseql₁✝l₁✝¹⊢ Subseq(x✝::l₁✝)(x✝::l₂✝)skipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath✝:Subseql₁l₁✝⊢ Subseql₁(x✝::l₂✝)·takel₂:ListNatl₃:ListNatx✝:Natl₁✝¹:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁✝:ListNath✝:Subseql₁✝l₁✝¹⊢ Subseq(x✝::l₁✝)(x✝::l₂✝)constructortakel₂:ListNatl₃:ListNatx✝:Natl₁✝¹:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁✝:ListNath✝:Subseql₁✝l₁✝¹⊢ Subseql₁✝l₂✝;applyihtakel₂:ListNatl₃:ListNatx✝:Natl₁✝¹:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁✝:ListNath✝:Subseql₁✝l₁✝¹⊢ Subseql₁✝l₁✝¹;assumptionAll goals completed! 🐙·skipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath✝:Subseql₁l₁✝⊢ Subseql₁(x✝::l₂✝)constructorskipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath✝:Subseql₁l₁✝⊢ Subseql₁l₂✝;applyihskipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝¹:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath✝:Subseql₁l₁✝⊢ Subseql₁l₁✝;assumptionAll goals completed! 🐙|skip_ih=>skipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath12:Subseql₁l₁✝⊢ Subseql₁(x✝::l₂✝)constructorskipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath12:Subseql₁l₁✝⊢ Subseql₁l₂✝;applyihskipl₂:ListNatl₃:ListNatx✝:Natl₁✝:ListNatl₂✝:ListNath✝:Subseql₁✝l₂✝ih:∀(l₁:ListNat),Subseql₁l₁✝→Subseql₁l₂✝l₁:ListNath12:Subseql₁l₁✝⊢ Subseql₁l₁✝;assumptionAll goals completed! 🐙endSubseq
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.
endRProvability2
Exercise★★(total_relation) (Optional)
Define an inductive binary relation TotalRelation that holds
between every pair of natural numbers.
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.
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.)
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.
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.
namespaceFilterChallengeopenLePlayground/- Here's a polymorphic version of `Subseq` we've seen above. -/inductiveSubseq{α:Type}:Listα→Listα→Propwhere|nil{l:Listα}:Subseq[]l|take{x:α}{l₁l₂:Listα}(h:Subseql₁l₂):Subseq(x::l₁)(x::l₂)|skip{x:α}{l₁l₂:Listα}(h:Subseql₁l₂):Subseql₁(x::l₂)/- A few lemmas about subseq. -/namespaceSubseqtheoremdrop_l{α:Type}{x:α}{l₁l₂:Listα}(h:Subseq(x::l₁)l₂):Subseql₁l₂:=byα:Typex:αl₁:Listαl₂:Listαh:Subseq(x::l₁)l₂⊢ Subseql₁l₂inductionl₂generalizingl₁with|nil=>nilα:Typex:αl₁:Listαh:Subseq(x::l₁)[]⊢ Subseql₁[]inversionhAll goals completed! 🐙|cons__ih=>consα:Typex:αhead✝:αtail✝:Listαih:∀{l₁:Listα},Subseq(x::l₁)tail✝→Subseql₁tail✝l₁:Listαh:Subseq(x::l₁)(head✝::tail✝)⊢ Subseql₁(head✝::tail✝)inversionhwith|takehs=>constructortakeα:Typex:αtail✝:Listαih:∀{l₁:Listα},Subseq(x::l₁)tail✝→Subseql₁tail✝l₁:Listαhs:Subseql₁tail✝⊢ Subseql₁tail✝;assumptionAll goals completed! 🐙|skiphs=>constructorskipα:Typex:αhead✝:αtail✝:Listαih:∀{l₁:Listα},Subseq(x::l₁)tail✝→Subseql₁tail✝l₁:Listαhs:Subseq(x::l₁)tail✝⊢ Subseql₁tail✝;applyihskipα:Typex:αhead✝:αtail✝:Listαih:∀{l₁:Listα},Subseq(x::l₁)tail✝→Subseql₁tail✝l₁:Listαhs:Subseq(x::l₁)tail✝⊢ Subseq(x::l₁)tail✝;assumptionAll goals completed! 🐙theoremdrop{α:Type}{x:α}{l₁l₂:Listα}(h:Subseq(x::l₁)(x::l₂)):Subseql₁l₂:=byα:Typex:αl₁:Listαl₂:Listαh:Subseq(x::l₁)(x::l₂)⊢ Subseql₁l₂inversionhwith|take=>assumptionAll goals completed! 🐙|skip=>applydrop_lskip.hα:Typex:αl₁:Listαl₂:Listαh✝:Subseq(x::l₁)l₂⊢ Subseq(?skip.x::l₁)l₂skip.xα:Typex:αl₁:Listαl₂:Listαh✝:Subseq(x::l₁)l₂⊢ α;assumptionAll goals completed! 🐙endSubseq/-- 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. -/defMaximal{α:Type}(maxList:Listα)(P:Listα→Prop):Prop:=PmaxList∧∀(l:Listα),Pl→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`. -/defGoodSubseq{α:Type}(test:α→Bool)(llsub:Listα):=Subseqlsubl∧lsub.allbtest/-- Good subsequences can be extended with good elements. -/theoremGoodSubseq.extend{α:Type}{x:α}{llsub:Listα}{test:α→Bool}(hx:testx=true)(h:GoodSubseqtestllsub):GoodSubseqtest(x::l)(x::lsub):=byα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=trueh:GoodSubseqtestllsub⊢ GoodSubseqtest(x::l)(x::lsub)obtain⟨hsub,hall⟩:=hα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ GoodSubseqtest(x::l)(x::lsub)constructorleftα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ Subseq(x::lsub)(x::l)rightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ List.allbtest(x::lsub)=true·leftα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ Subseq(x::lsub)(x::l)constructorleftα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ Subseqlsubl;assumptionAll goals completed! 🐙·rightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ List.allbtest(x::lsub)=truerw[List.allb_cons,rightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ (testx&&List.allbtestlsub)=trueBool.and_eq_truerightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ testx=true∧List.allbtestlsub=true]rightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ testx=true∧List.allbtestlsub=trueconstructorright.leftα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ testx=trueright.rightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ List.allbtestlsub=true·right.leftα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ testx=trueassumptionAll goals completed! 🐙·right.rightα:Typex:αl:Listαlsub:Listαtest:α→Boolhx:testx=truehsub:Subseqlsublhall:List.allbtestlsub=true⊢ List.allbtestlsub=trueassumptionAll 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`. -/theoremmaximal_strengthening{α:Type}{x:α}{maxListl:Listα}{test:α→Bool}(hx:testx=false)(h:MaximalmaxList(GoodSubseqtest(x::l))):MaximalmaxList(GoodSubseqtestl):=byα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falseh:MaximalmaxList(GoodSubseqtest(x::l))⊢ MaximalmaxList(GoodSubseqtestl)obtain⟨⟨hsub,hall⟩,hlen⟩:=hα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ MaximalmaxList(GoodSubseqtestl)constructorleftα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ GoodSubseqtestlmaxListrightα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤maxList.lengthconstructorleft.leftα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ SubseqmaxListlleft.rightα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ List.allbtestmaxList=truerightα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤maxList.length·left.leftα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ SubseqmaxListlinversionhsubwith|nil=>constructorAll goals completed! 🐙|takel₁hsub=>rw[List.allb_cons,takeα:Typex:αl:Listαtest:α→Boolhx:testx=falsel₁:Listαhlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤(x::l₁).lengthhall:(testx&&List.allbtestl₁)=truehsub:Subseql₁l⊢ Subseq(x::l₁)lBool.and_eq_truetakeα:Typex:αl:Listαtest:α→Boolhx:testx=falsel₁:Listαhlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤(x::l₁).lengthhall:testx=true∧List.allbtestl₁=truehsub:Subseql₁l⊢ Subseq(x::l₁)l]athalltakeα:Typex:αl:Listαtest:α→Boolhx:testx=falsel₁:Listαhlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤(x::l₁).lengthhall:testx=true∧List.allbtestl₁=truehsub:Subseql₁l⊢ Subseq(x::l₁)lobtain⟨ht,_⟩:=halltakeα:Typex:αl:Listαtest:α→Boolhx:testx=falsel₁:Listαhlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤(x::l₁).lengthhsub:Subseql₁lht:testx=trueright✝:List.allbtestl₁=true⊢ Subseq(x::l₁)lrw[hxtakeα:Typex:αl:Listαtest:α→Boolhx:testx=falsel₁:Listαhlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤(x::l₁).lengthhsub:Subseql₁lht:false=trueright✝:List.allbtestl₁=true⊢ Subseq(x::l₁)l]athttakeα:Typex:αl:Listαtest:α→Boolhx:testx=falsel₁:Listαhlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤(x::l₁).lengthhsub:Subseql₁lht:false=trueright✝:List.allbtestl₁=true⊢ Subseq(x::l₁)l;contradictionAll goals completed! 🐙|skip=>assumptionAll goals completed! 🐙·left.rightα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ List.allbtestmaxList=trueassumptionAll goals completed! 🐙·rightα:Typex:αmaxList:Listαl:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=true⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤maxList.lengthintrol⟨hsub',hall'⟩rightα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ l.length≤maxList.length;applyhlenrightα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ GoodSubseqtest(x::l✝)l;constructorright.leftα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ Subseql(x::l✝)right.rightα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ List.allbtestl=true·right.leftα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ Subseql(x::l✝)constructorright.leftα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ Subseqll✝;assumptionAll goals completed! 🐙·right.rightα:Typex:αmaxList:Listαl✝:Listαtest:α→Boolhx:testx=falsehlen:∀(l_1:Listα),GoodSubseqtest(x::l)l_1→l_1.length≤maxList.lengthhsub:SubseqmaxList(x::l)hall:List.allbtestmaxList=truel:Listαhsub':Subseqll✝hall':List.allbtestl=true⊢ List.allbtestl=trueassumptionAll goals completed! 🐙/- Some easy lemmas about filter: its result is a good subsequence of
the original list. -/theoremfilter_subseq{α:Type}(l:Listα)(test:α→Bool):Subseq(filtertestl)l:=byα:Typel:Listαtest:α→Bool⊢ Subseq(filtertestl)linductionlwith|nil=>nilα:Typetest:α→Bool⊢ Subseq(filtertest[])[]rw[filter_nilnilα:Typetest:α→Bool⊢ Subseq[][]]nilα:Typetest:α→Bool⊢ Subseq[][];constructorAll goals completed! 🐙|consxxsih=>consα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xs⊢ Subseq(filtertest(x::xs))(x::xs)casesh:(testx)cons.falseα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=false⊢ Subseq(filtertest(x::xs))(x::xs)cons.trueα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=true⊢ Subseq(filtertest(x::xs))(x::xs)·cons.falseα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=false⊢ Subseq(filtertest(x::xs))(x::xs)rw[filter_cons_of_neghcons.falseα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=false⊢ Subseq(filtertestxs)(x::xs)]cons.falseα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=false⊢ Subseq(filtertestxs)(x::xs)constructorcons.falseα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=false⊢ Subseq(filtertestxs)xs;assumptionAll goals completed! 🐙·cons.trueα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=true⊢ Subseq(filtertest(x::xs))(x::xs)rw[filter_cons_of_poshcons.trueα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=true⊢ Subseq(x::filtertestxs)(x::xs)]cons.trueα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=true⊢ Subseq(x::filtertestxs)(x::xs)constructorcons.trueα:Typetest:α→Boolx:αxs:Listαih:Subseq(filtertestxs)xsh:testx=true⊢ Subseq(filtertestxs)xs;assumptionAll goals completed! 🐙theoremfilter_all{α:Type}(l:Listα)(test:α→Bool):(filtertestl).allbtest:=byα:Typel:Listαtest:α→Bool⊢ List.allbtest(filtertestl)=trueinductionlwith|nil=>nilα:Typetest:α→Bool⊢ List.allbtest(filtertest[])=truerflAll goals completed! 🐙|consxxsih=>consα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=true⊢ List.allbtest(filtertest(x::xs))=truecasesh:(testx)cons.falseα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=false⊢ List.allbtest(filtertest(x::xs))=truecons.trueα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ List.allbtest(filtertest(x::xs))=true·cons.falseα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=false⊢ List.allbtest(filtertest(x::xs))=truerw[filter_cons_of_neghcons.falseα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=false⊢ List.allbtest(filtertestxs)=true]cons.falseα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=false⊢ List.allbtest(filtertestxs)=true;assumptionAll goals completed! 🐙·cons.trueα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ List.allbtest(filtertest(x::xs))=truerw[filter_cons_of_posh,cons.trueα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ List.allbtest(x::filtertestxs)=trueList.allb_cons,cons.trueα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ (testx&&List.allbtest(filtertestxs))=trueBool.and_eq_truecons.trueα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ testx=true∧List.allbtest(filtertestxs)=true]cons.trueα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ testx=true∧List.allbtest(filtertestxs)=trueconstructorcons.true.leftα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ testx=truecons.true.rightα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ List.allbtest(filtertestxs)=true·cons.true.leftα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ testx=trueassumptionAll goals completed! 🐙·cons.true.rightα:Typetest:α→Boolx:αxs:Listαih:List.allbtest(filtertestxs)=trueh:testx=true⊢ List.allbtest(filtertestxs)=trueassumptionAll goals completed! 🐙/- And now for the main theorem: `lsub` is a maximal good subsequence
of `l` if and only if `filter test l = lsub` -/theoremfilter_spec2{α:Type}(llsub:Listα)(test:α→Bool):Maximallsub(GoodSubseqtestl)↔filtertestl=lsub:=byα:Typel:Listαlsub:Listαtest:α→Bool⊢ Maximallsub(GoodSubseqtestl)↔filtertestl=lsubconstructormpα:Typel:Listαlsub:Listαtest:α→Bool⊢ Maximallsub(GoodSubseqtestl)→filtertestl=lsubmprα:Typel:Listαlsub:Listαtest:α→Bool⊢ filtertestl=lsub→Maximallsub(GoodSubseqtestl)·mpα:Typel:Listαlsub:Listαtest:α→Bool⊢ Maximallsub(GoodSubseqtestl)→filtertestl=lsubinductionlgeneralizinglsubwith|nil=>mp.nilα:Typetest:α→Boollsub:Listα⊢ Maximallsub(GoodSubseqtest[])→filtertest[]=lsubintro⟨⟨hsub,hall⟩,hlen⟩mp.nilα:Typetest:α→Boollsub:Listαhsub:Subseqlsub[]hall:List.allbtestlsub=truehlen:∀(l:Listα),GoodSubseqtest[]l→l.length≤lsub.length⊢ filtertest[]=lsubinversionhsubnilα:Typetest:α→Boolhall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest[]l→l.length≤[].length⊢ filtertest[]=[]rw[filter_nilnilα:Typetest:α→Boolhall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest[]l→l.length≤[].length⊢ []=[]]All goals completed! 🐙|consxxsih=>mp.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listα⊢ Maximallsub(GoodSubseqtest(x::xs))→filtertest(x::xs)=lsubcaseshtest:testxwith|false=>mp.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=false⊢ Maximallsub(GoodSubseqtest(x::xs))→filtertest(x::xs)=lsubrw[filter_cons_of_neghtestmp.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=false⊢ Maximallsub(GoodSubseqtest(x::xs))→filtertestxs=lsub]mp.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=false⊢ Maximallsub(GoodSubseqtest(x::xs))→filtertestxs=lsub·mp.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=false⊢ Maximallsub(GoodSubseqtest(x::xs))→filtertestxs=lsubintrohmaxmp.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=falsehmax:Maximallsub(GoodSubseqtest(x::xs))⊢ filtertestxs=lsub;applyihmp.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=falsehmax:Maximallsub(GoodSubseqtest(x::xs))⊢ Maximallsub(GoodSubseqtestxs)applymaximal_strengtheninghtesthmaxAll goals completed! 🐙|true=>mp.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=true⊢ Maximallsub(GoodSubseqtest(x::xs))→filtertest(x::xs)=lsubintro⟨⟨hsub,hall⟩,hlen⟩mp.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsublsub:Listαhtest:testx=truehsub:Subseqlsub(x::xs)hall:List.allbtestlsub=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤lsub.length⊢ filtertest(x::xs)=lsub/- in this case, `lsub` must begin with `x`, since otherwise it
wouldn't be maximal. -/caseslsubwith|nil=>mp.cons.true.nilα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ filtertest(x::xs)=[]-- lsub = [] (impossible: contradicts maximality of lsub)havecontra:[x].length≤([]:Listα).length:=byα:Typel:Listαlsub:Listαtest:α→Bool⊢ Maximallsub(GoodSubseqtestl)↔filtertestl=lsubapplyhlenα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ GoodSubseqtest(x::xs)[x]constructorleftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ Subseq[x](x::xs)rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ List.allbtest[x]=true·leftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ Subseq[x](x::xs)constructorleftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ Subseq[]xs;constructorAll goals completed! 🐙·rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ List.allbtest[x]=truerw[List.allb_cons,rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ (testx&&List.allbtest[])=trueList.allb_nil,rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ (testx&&true)=trueBool.and_truerightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ testx=true]rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].length⊢ testx=trueassumptionmp.cons.true.nilα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truehsub:Subseq[](x::xs)hall:List.allbtest[]=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤[].lengthcontra:[x].length≤[].length⊢ filtertest(x::xs)=[]contradictionAll goals completed! 🐙|consx'xs'=>mp.cons.true.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhsub:Subseq(x'::xs')(x::xs)hall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').length⊢ filtertest(x::xs)=x'::xs'haveheq:x=x':=byα:Typel:Listαlsub:Listαtest:α→Bool⊢ Maximallsub(GoodSubseqtestl)↔filtertestl=lsub-- because of maximality againinversionhsubwith|takehsub=>rflAll goals completed! 🐙|skiphsub=>-- contradiction, since x :: x' :: xs' would be longerhavecontra:(x::x'::xs').length≤(x'::xs').length:=byα:Typel:Listαlsub:Listαtest:α→Bool⊢ Maximallsub(GoodSubseqtestl)↔filtertestl=lsubapplyhlenα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ GoodSubseqtest(x::xs)(x::x'::xs');constructorleftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ Subseq(x::x'::xs')(x::xs)rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ List.allbtest(x::x'::xs')=true·leftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ Subseq(x::x'::xs')(x::xs)constructorleftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ Subseq(x'::xs')xs;assumptionAll goals completed! 🐙·rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ List.allbtest(x::x'::xs')=truerw[List.allb_cons,rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ (testx&&List.allbtest(x'::xs'))=trueList.allb_cons,rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ (testx&&(testx'&&List.allbtestxs'))=trueBool.and_eq_true,rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=true∧(testx'&&List.allbtestxs')=trueBool.and_eq_truerightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=true∧testx'=true∧List.allbtestxs'=true]rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=true∧testx'=true∧List.allbtestxs'=truerw[List.allb_cons,rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:(testx'&&List.allbtestxs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=true∧testx'=true∧List.allbtestxs'=trueBool.and_eq_truerightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:testx'=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=true∧testx'=true∧List.allbtestxs'=true]athallrightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:testx'=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=true∧testx'=true∧List.allbtestxs'=trueconstructorright.leftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:testx'=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx=trueright.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:testx'=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx'=true∧List.allbtestxs'=true;assumptionright.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:testx'=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xs⊢ testx'=true∧List.allbtestxs'=true;assumptionskipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xscontra:(x::x'::xs').length≤(x'::xs').length⊢ x=x'rw[List.length_cons,skipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xscontra:(x'::xs').length+1≤(x'::xs').length⊢ x=x'List.length_consskipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xscontra:xs'.length+1+1≤xs'.length+1⊢ x=x']atcontraskipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xscontra:xs'.length+1+1≤xs'.length+1⊢ x=x'applyle_of_succ_le_succatcontraskipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xscontra:xs'.length+1≤xs'.length⊢ x=x'applyle_not_succ_le_selfatcontraskipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthhsub:Subseq(x'::xs')xscontra:False⊢ x=x'contradictionmp.cons.true.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truex':αxs':Listαhsub:Subseq(x'::xs')(x::xs)hall:List.allbtest(x'::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x'::xs').lengthheq:x=x'⊢ filtertest(x::xs)=x'::xs'substheqmp.cons.true.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ filtertest(x::xs)=x::xs';rw[filter_cons_of_poshtestmp.cons.true.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ x::filtertestxs=x::xs']mp.cons.true.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ x::filtertestxs=x::xs'congrmp.cons.true.cons.e_tailα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ filtertestxs=xs';applyihmp.cons.true.cons.e_tailα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ Maximalxs'(GoodSubseqtestxs);constructormp.cons.true.cons.e_tail.leftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ GoodSubseqtestxsxs'mp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ ∀(l:Listα),GoodSubseqtestxsl→l.length≤xs'.length;constructormp.cons.true.cons.e_tail.left.leftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ Subseqxs'xsmp.cons.true.cons.e_tail.left.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ List.allbtestxs'=truemp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ ∀(l:Listα),GoodSubseqtestxsl→l.length≤xs'.length·mp.cons.true.cons.e_tail.left.leftα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ Subseqxs'xsexacthsub.dropAll goals completed! 🐙·mp.cons.true.cons.e_tail.left.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ List.allbtestxs'=truerw[List.allb_cons,mp.cons.true.cons.e_tail.left.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:(testx&&List.allbtestxs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ List.allbtestxs'=trueBool.and_eq_truemp.cons.true.cons.e_tail.left.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:testx=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ List.allbtestxs'=true]athallmp.cons.true.cons.e_tail.left.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:testx=true∧List.allbtestxs'=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ List.allbtestxs'=trueobtain⟨_,_⟩:=hallmp.cons.true.cons.e_tail.left.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').lengthleft✝:testx=trueright✝:List.allbtestxs'=true⊢ List.allbtestxs'=true;assumptionAll goals completed! 🐙·mp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').length⊢ ∀(l:Listα),GoodSubseqtestxsl→l.length≤xs'.lengthintrol'hgoodmp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤(x::xs').lengthl':Listαhgood:GoodSubseqtestxsl'⊢ l'.length≤xs'.lengthrw[List.length_consmp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤xs'.length+1l':Listαhgood:GoodSubseqtestxsl'⊢ l'.length≤xs'.length]athlenmp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤xs'.length+1l':Listαhgood:GoodSubseqtestxsl'⊢ l'.length≤xs'.lengthapplyle_of_succ_le_succmp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤xs'.length+1l':Listαhgood:GoodSubseqtestxsl'⊢ l'.length+1≤xs'.length+1applyhlen(x::l')mp.cons.true.cons.e_tail.rightα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),Maximallsub(GoodSubseqtestxs)→filtertestxs=lsubhtest:testx=truexs':Listαhsub:Subseq(x::xs')(x::xs)hall:List.allbtest(x::xs')=truehlen:∀(l:Listα),GoodSubseqtest(x::xs)l→l.length≤xs'.length+1l':Listαhgood:GoodSubseqtestxsl'⊢ GoodSubseqtest(x::xs)(x::l')exacthgood.extendhtestAll goals completed! 🐙·mprα:Typel:Listαlsub:Listαtest:α→Bool⊢ filtertestl=lsub→Maximallsub(GoodSubseqtestl)introhfiltermprα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ Maximallsub(GoodSubseqtestl)constructormpr.leftα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ GoodSubseqtestllsubmpr.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤lsub.length;rw[←hfiltermpr.leftα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ GoodSubseqtestl(filtertestl)mpr.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤lsub.length]mpr.leftα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ GoodSubseqtestl(filtertestl)mpr.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤lsub.lengthconstructormpr.left.leftα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ Subseq(filtertestl)lmpr.left.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ List.allbtest(filtertestl)=truempr.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤lsub.length·mpr.left.leftα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ Subseq(filtertestl)lapplyfilter_subseqAll goals completed! 🐙·mpr.left.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ List.allbtest(filtertestl)=trueapplyfilter_allAll goals completed! 🐙·mpr.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsub⊢ ∀(l_1:Listα),GoodSubseqtestll_1→l_1.length≤lsub.lengthintrol'⟨hsub,hall⟩mpr.rightα:Typel:Listαlsub:Listαtest:α→Boolhfilter:filtertestl=lsubl':Listαhsub:Subseql'lhall:List.allbtestl'=true⊢ l'.length≤lsub.lengthinductionlgeneralizingl'lsubwith|nil=>mpr.right.nilα:Typetest:α→Boollsub:Listαhfilter:filtertest[]=lsubl':Listαhsub:Subseql'[]hall:List.allbtestl'=true⊢ l'.length≤lsub.lengthinversionhsubnilα:Typetest:α→Boollsub:Listαhfilter:filtertest[]=lsubhall:List.allbtest[]=true⊢ [].length≤lsub.length;rw[List.length_nilnilα:Typetest:α→Boollsub:Listαhfilter:filtertest[]=lsubhall:List.allbtest[]=true⊢ 0≤lsub.length]nilα:Typetest:α→Boollsub:Listαhfilter:filtertest[]=lsubhall:List.allbtest[]=true⊢ 0≤lsub.lengthapplyzero_leAll goals completed! 🐙|consxxsih=>mpr.right.consα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertest(x::xs)=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=true⊢ l'.length≤lsub.lengthcaseshtest:testxwith|false=>mpr.right.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertest(x::xs)=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=false⊢ l'.length≤lsub.lengthrw[filter_cons_of_neghtestmpr.right.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=false⊢ l'.length≤lsub.length]athfiltermpr.right.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=false⊢ l'.length≤lsub.length·mpr.right.cons.falseα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=false⊢ l'.length≤lsub.lengthapplyih_hfilter__hallα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=false⊢ Subseql'xsinversionhsubwith|nil=>constructorAll goals completed! 🐙|takelhsub=>rw[List.allb_cons,takeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubhtest:testx=falsel:Listαhall:(testx&&List.allbtestl)=truehsub:Subseqlxs⊢ Subseq(x::l)xsBool.and_eq_truetakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubhtest:testx=falsel:Listαhall:testx=true∧List.allbtestl=truehsub:Subseqlxs⊢ Subseq(x::l)xs]athalltakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubhtest:testx=falsel:Listαhall:testx=true∧List.allbtestl=truehsub:Subseqlxs⊢ Subseq(x::l)xsobtain⟨ht,_⟩:=halltakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubhtest:testx=falsel:Listαhsub:Subseqlxsht:testx=trueright✝:List.allbtestl=true⊢ Subseq(x::l)xsrw[httakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubhtest:true=falsel:Listαhsub:Subseqlxsht:testx=trueright✝:List.allbtestl=true⊢ Subseq(x::l)xs]athtesttakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertestxs=lsubhtest:true=falsel:Listαhsub:Subseqlxsht:testx=trueright✝:List.allbtestl=true⊢ Subseq(x::l)xscontradictionAll goals completed! 🐙|skiphsub=>assumptionAll goals completed! 🐙|true=>mpr.right.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:filtertest(x::xs)=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=true⊢ l'.length≤lsub.lengthrw[filter_cons_of_poshtestmpr.right.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=true⊢ l'.length≤lsub.length]athfiltermpr.right.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=true⊢ l'.length≤lsub.lengthrw[←hfilter,mpr.right.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=true⊢ l'.length≤(x::filtertestxs).lengthList.length_consmpr.right.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=true⊢ l'.length≤(filtertestxs).length+1]mpr.right.cons.trueα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubl':Listαhsub:Subseql'(x::xs)hall:List.allbtestl'=truehtest:testx=true⊢ l'.length≤(filtertestxs).length+1inversionhsubwith|nil=>rw[List.length_nilnilα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truehall:List.allbtest[]=true⊢ 0≤(filtertestxs).length+1]nilα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truehall:List.allbtest[]=true⊢ 0≤(filtertestxs).length+1;applyzero_leAll goals completed! 🐙|takelhsub=>rw[List.length_constakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:List.allbtest(x::l)=truehsub:Subseqlxs⊢ l.length+1≤(filtertestxs).length+1]takeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:List.allbtest(x::l)=truehsub:Subseqlxs⊢ l.length+1≤(filtertestxs).length+1applysucc_le_succtakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:List.allbtest(x::l)=truehsub:Subseqlxs⊢ l.length≤(filtertestxs).lengthapplyih_rfl_hsubtakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:List.allbtest(x::l)=truehsub:Subseqlxs⊢ List.allbtestl=truerw[List.allb_cons,takeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:(testx&&List.allbtestl)=truehsub:Subseqlxs⊢ List.allbtestl=trueBool.and_eq_truetakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:testx=true∧List.allbtestl=truehsub:Subseqlxs⊢ List.allbtestl=true]athalltakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhall:testx=true∧List.allbtestl=truehsub:Subseqlxs⊢ List.allbtestl=trueobtain⟨_,h⟩:=halltakeα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubhtest:testx=truel:Listαhsub:Subseqlxsleft✝:testx=trueh:List.allbtestl=true⊢ List.allbtestl=trueexacthAll goals completed! 🐙|skiphsub=>applyLe.stepskipα:Typetest:α→Boolx:αxs:Listαih:∀(lsub:Listα),filtertestxs=lsub→∀(l':Listα),Subseql'xs→List.allbtestl'=true→l'.length≤lsub.lengthlsub:Listαhfilter:x::filtertestxs=lsubl':Listαhall:List.allbtestl'=truehtest:testx=truehsub:Subseql'xs⊢ l'.length≤(filtertestxs).lengthexactih_rfl_hsubhallAll goals completed! 🐙endFilterChallenge
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:
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.
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.
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.
theoremList.mem_split{α:Type}{x:α}{l:Listα}(hin:x∈l):∃l₁l₂,l=l₁++x::l₂:=byα: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 libraryinductionlgeneralizingxwith|nil=>nilα:Typex:αhin:x∈[]⊢ ∃l₁l₂,[]=l₁++x::l₂rw[List.mem_nil_iffnilα:Typex:αhin:False⊢ ∃l₁l₂,[]=l₁++x::l₂]athinnilα:Typex:αhin:False⊢ ∃l₁l₂,[]=l₁++x::l₂;contradictionAll goals completed! 🐙|consx'xs'ih=>consα: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₂rw[List.mem_consconsα: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₂]athinconsα: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₂caseshinwith|inlhin=>cons.inlα:Typex':αxs':Listαih:∀{x:α},x∈xs'→∃l₁l₂,xs'=l₁++x::l₂x:αhin:x=x'⊢ ∃l₁l₂,x'::xs'=l₁++x::l₂substhincons.inlα:Typexs':Listαih:∀{x:α},x∈xs'→∃l₁l₂,xs'=l₁++x::l₂x:α⊢ ∃l₁l₂,x::xs'=l₁++x::l₂exists[]cons.inlα:Typexs':Listαih:∀{x:α},x∈xs'→∃l₁l₂,xs'=l₁++x::l₂x:α⊢ ∃l₂,x::xs'=[]++x::l₂existsxs'All goals completed! 🐙|inrhin=>cons.inrα:Typex':αxs':Listαih:∀{x:α},x∈xs'→∃l₁l₂,xs'=l₁++x::l₂x:αhin:x∈xs'⊢ ∃l₁l₂,x'::xs'=l₁++x::l₂have⟨l₁',⟨l₂',ih⟩⟩:=ihhincons.inrα: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₂substihcons.inrα: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₂existsx'::l₁'cons.inrα: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₂existsl₂'All goals completed! 🐙
Now define a property Repeats such that Repeats l asserts
that l contains at least one repeated element.
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.
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.