# Functional induction

Proving properties of recursive functions is usually easiest when the proof follows the structure of the function that is being verified – same case splitting, same recursion pattern. To avoid having to spell this out in every proof, the upcoming 4.8 release of Lean provides a functional induction principle for recursive functions. Used with the induction tactic, this takes care of the repetitive parts of the proof.

In this blog post we will look at examples of recursive functions where this feature can be convenient, how to use it, and where its limits lie. In particular, we look at functions with fixed parameters and overlapping patterns and mutually recursive functions, where this feature is especially useful. Because some recursive functions can cause the simp tactic to loop, as it tries to unfold the definition infinitely many times, we also examine techniques for avoiding infinite simplification.

The examples in this post are short, in order to demonstrate the feature. If the gain does not seem to be very great, keep in mind that we are looking at particularly simple examples here. More realistic applications may have more cases or explicit termination proofs, and in these cases, functional induction can lead to shorter, more maintainable proofs.

## Alternating lists, the manual way

We begin with a function that picks elements from two alternating input lists:

def alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α : (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) → ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 | [], ysList α => ysList α | xα::xsList α, ysList α => xα :: alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α termination_bySpecify a termination argument for well-founded termination:  termination_by a - b  indicates that termination of the currently defined recursive function follows because the difference between the the arguments a and b. If the fuction takes further argument after the colon, you can name them as follows:  def example (a : Nat) : Nat → Nat → Nat := termination_by b c => a - b  If omitted, a termination argument will be inferred. If written as termination_by?, the inferrred termination argument will be suggested. xsList α ysList α => xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. 

It seems to be doing what it should:

["red", "apple", "green", "banana", "blue", "yellow"] #eval alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ["red""red" : String, "green""green" : String, "blue""blue" : String, "yellow""yellow" : String] ["apple""apple" : String, "banana""banana" : String]
["red", "apple", "green", "banana", "blue", "yellow"]


Now let us try to prove a simple theorem about alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α, namely that the length of the resulting list is the sum of the argument lists' lengths.

The alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α function is not structurally recursive, so using induction xs is not going to be helpful (try it if you are curious how it fails). Instead, we can attempt to write a recursive theorem: we use the theorem we are in the process of proving in the proof, and then rely on Lean's termination checker to check that this is actually ok.

So a first attempt at the proof might start by unfolding the definition of alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α. This exposes a match expression, and our Lean muscle memory tells us to proceed using split. The first case is now trivial. In the second case we use the next tactic to name the new variables. Then we call the theorem we are currently proving on the smaller lists ys and xs, following the pattern of alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α itself, and after some simplification and arithmetic, this goal is closed. Lean will tell us that it cannot prove termination of the recursive call, so we help it by giving the same termination argument as we gave to alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α itself.

theorem length_alternate1length_alternate1.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length unfold* unfold id unfolds definition id. * unfold id1 id2 ... is equivalent to unfold id1; unfold id2; .... For non-recursive definitions, this tactic is identical to delta. For definitions by pattern matching, it uses "equation lemmas" which are autogenerated for each match arm. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List ααType u_1:Type u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (match xsList α, ysList α with | [], ysList α => ysList α | xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α, ysList α => xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length splitThe split tactic is useful for breaking nested if-then-else and match expressions into separate cases. For a match expression with n cases, the split tactic generates at most n subgoals. For example, given n : Nat, and a target if n = 0 then Q else R, split will generate one goal with hypothesis n = 0 and target Q, and a second goal with hypothesis ¬n = 0 and target R. Note that the introduced hypothesis is unnamed, and is commonly renamed used the case or next tactics. - split will split the goal (target). - split at h will split the hypothesis h. αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝¹List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ ysList α.length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.lengthh_2αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝²List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝¹List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝α:αType u_1xs✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xs✝List α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length nextnext => tac focuses on the next goal and solves it using tac, or else fails. next x₁ ... xₙ => tac additionally renames the n most recent hypotheses with inaccessible names to the given names. =>αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝¹List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ ysList α.length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. All goals completed! 🐙 nextnext => tac focuses on the next goal and solves it using tac, or else fails. next x₁ ... xₙ => tac additionally renames the n most recent hypotheses with inaccessible names to the given names. xα xsList α =>αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝¹List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length haveThe have tactic is for adding hypotheses to the local context of the main goal. * have h : t := e adds the hypothesis h : t if e is a term of type t. * have h := e uses the type of e for t. * have : t := e and have := e use this for the name of the hypothesis. * have pat := e for a pattern pat is equivalent to match e with | pat => _, where _ stands for the tactics that follow this one. It is convenient for types that have only one applicable constructor. For example, given h : p ∧ q ∧ r, have ⟨h₁, h₂, h₃⟩ := h produces the hypotheses h₁ : p, h₂ : q, and h₃ : r. ih(alternate ys xs).length = ys.length + xs.length := length_alternate1length_alternate1.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length ysList α xsList ααType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝¹List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [ih(alternate ys xs).length = ys.length + xs.length]αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝¹List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1x✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙 termination_bySpecify a termination argument for well-founded termination:  termination_by a - b  indicates that termination of the currently defined recursive function follows because the difference between the the arguments a and b. If the fuction takes further argument after the colon, you can name them as follows:  def example (a : Nat) : Nat → Nat → Nat := termination_by b c => a - b  If omitted, a termination argument will be inferred. If written as termination_by?, the inferrred termination argument will be suggested. xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. 

This works, but is not quite satisfactory. The split tactic is rather powerful, but having to name the variables with next is tedious. A more elegant variant is to use the same pattern matching in the proof as we do in the function definition:

theorem length_alternate2length_alternate2.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length : (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) → (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. | [], ysList α => byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α [] ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α]All goals completed! 🐙 | xα::xsList α, ysList α => byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α) ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length haveThe have tactic is for adding hypotheses to the local context of the main goal. * have h : t := e adds the hypothesis h : t if e is a term of type t. * have h := e uses the type of e for t. * have : t := e and have := e use this for the name of the hypothesis. * have pat := e for a pattern pat is equivalent to match e with | pat => _, where _ stands for the tactics that follow this one. It is convenient for types that have only one applicable constructor. For example, given h : p ∧ q ∧ r, have ⟨h₁, h₂, h₃⟩ := h produces the hypotheses h₁ : p, h₂ : q, and h₃ : r. ih(alternate ys xs).length = ys.length + xs.length := length_alternate2length_alternate2.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length ysList α xsList ααType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α) ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α, ih(alternate ys xs).length = ys.length + xs.length]αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙 termination_bySpecify a termination argument for well-founded termination:  termination_by a - b  indicates that termination of the currently defined recursive function follows because the difference between the the arguments a and b. If the fuction takes further argument after the colon, you can name them as follows:  def example (a : Nat) : Nat → Nat → Nat := termination_by b c => a - b  If omitted, a termination argument will be inferred. If written as termination_by?, the inferrred termination argument will be suggested. xsList α ysList α => xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. 

Note that we no longer unfold the function definition, but let the simplifier reduce the function application in the respective cases.

This style brings us closer to the function to be defined, but we still have to repeat the (possibly complex) pattern match of the original function, and the (possibly complex) termination argument and proof. Moreover, we have to explicitly instantiate the recursive invocation of the theorem, or hope that simp will only use it at the arguments that we want it to.

This is not very satisfying. The repetition makes the proof brittle, as a change to the function definition may require applying the same change to every single proof. We already explained the structure of the function to Lean once, we should not have to do it again!

## Alternating lists, with functional induction

This is where the functional induction theorem comes in. For every recursive function, Lean defines a functional induction principle; just append .induct to the name. In our example, it has the following type:

alternate.inductalternate.induct.{u_1} {α : Type u_1} (motive : List α → List α → Prop) (case1 : ∀ (ys : List α), motive [] ys) (case2 : ∀ (x : α) (xs ys : List α), motive ys xs → motive (x :: xs) ys) (xs ys : List α) : motive xs ys {αType u_1 : TypeA type universe. Type ≡ Type 0, Type u ≡ Sort (u + 1). _} (motiveList α → List α → Prop : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 → ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 → Prop) (case1∀ (ys : List α), motive [] ys : ∀ (ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1), motiveList α → List α → Prop [] ysList α) (case2∀ (x : α) (xs ys : List α), motive ys xs → motive (x :: xs) ys : ∀ (xα : αType u_1) (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1), motiveList α → List α → Prop ysList α xsList α → motiveList α → List α → Prop (xα :: xsList α) ysList α) (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : motiveList α → List α → Prop xsList α ysList α

Seeing an induction principle in its full glory can be a bit intimidating, so let us spell it out in prose:

• Given a property of two lists that we want to prove (the motive),

• if it holds when the first list is empty (the minor premise case1, corresponding to the first branch of alternate),

• and if it holds when the first list is non-empty (the minor premise case2, corresponding to the second branch of alternate), where we may assume that it holds for the second list and the tail of the first list (the induction hypothesis, corresponding to the recursive call in alternate),

• then given two arbitrary lists (the targets xs and xy, which correspond to the varying parameters of the function)

• the property holds for these lists.

An induction principle like this is used every time you use the induction tactic; for example induction on natural numbers uses Nat.recAuxNat.recAux.{u} {motive : Nat → Sort u} (zero : motive 0) (succ : (n : Nat) → motive n → motive (n + 1)) (t : Nat) : motive tRecursor identical to Nat.rec but uses notations 0 for Nat.zero and · + 1 for Nat.succ. Used as the default Nat eliminator by the induction tactic. .

With this bespoke induction principle, we can now use the induction tactic in our proof:

theorem length_alternate3length_alternate3.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList α, ysList α using alternate.inductalternate.induct.{u_1} {α : Type u_1} (motive : List α → List α → Prop) (case1 : ∀ (ys : List α), motive [] ys) (case2 : ∀ (x : α) (xs ys : List α), motive ys xs → motive (x :: xs) ys) (xs ys : List α) : motive xs ys withAfter with, there is an optional tactic that runs on all branches, and then a list of alternatives. | case1 ysList α =>case1αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α [] ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α]All goals completed! 🐙 | case2 xα xsList α ysList α ih(alternate ys xs).length = ys.length + xs.length =>case2αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α) ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α, ih(alternate ys xs).length = ys.length + xs.length]case2αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙

This looks still quite similar to the previous iteration, but note the absence of a termination argument. Furthermore, just like with normal induction, we were provided the induction hypothesis already specialized at the right argument, as you can see in the proof state after case2.

With all this boilerplate out of the way, chances are good that we can now solve both cases with just proof automation tactics, leading to the following two-line proof:

theorem length_alternate4length_alternate4.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList α, ysList α using alternate.inductalternate.induct.{u_1} {α : Type u_1} (motive : List α → List α → Prop) (case1 : ∀ (ys : List α), motive [] ys) (case2 : ∀ (x : α) (xs ys : List α), motive ys xs → motive (x :: xs) ys) (xs ys : List α) : motive xs yscase1αType u_1:Type u_1ys✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α [] ys✝List α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ys✝List α.lengthcase2αType u_1:Type u_1x✝α:αType u_1xs✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ys✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih1✝(alternate ys✝ xs✝).length = ys✝.length + xs✝.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ys✝List α xs✝List α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ys✝List α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xs✝List α.length⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α) ys✝List α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ys✝List α.length all_goalsall_goals tac runs tac on each goal, concatenating the resulting goals, if any. simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α, *]case2αType u_1:Type u_1x✝α:αType u_1xs✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ys✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih1✝(alternate ys✝ xs✝).length = ys✝.length + xs✝.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ys✝List α xs✝List α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ys✝List α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xs✝List α.length⊢ ys✝List α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xs✝List α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xs✝List α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ys✝List α.length; trytry tac runs tac and succeeds even if tac failed. omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙

The * in simp's argument list indicates that simp should also consider local assumptions for rewriting; this way it picks up the induction hypothesis.

In terms of proof automation, this is almost as good as it can get: We state the theorem and the induction and leave the rest to Lean's automation. However, just in case you prefer explicit readable and educational proofs, we can of course also spell it out explicitly:

theorem length_alternate5length_alternate5.{u_1} {α : Type u_1} (xs ys : List α) : (alternate xs ys).length = xs.length + ys.length (xsList α ysList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α xsList α ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList α, ysList α using alternate.inductalternate.induct.{u_1} {α : Type u_1} (motive : List α → List α → Prop) (case1 : ∀ (ys : List α), motive [] ys) (case2 : ∀ (x : α) (xs ys : List α), motive ys xs → motive (x :: xs) ys) (xs ys : List α) : motive xs ys withAfter with, there is an optional tactic that runs on all branches, and then a list of alternatives. | case1 ysList α =>case1αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α [] ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length calcStep-wise reasoning over transitive relations.  calc a = b := pab b = c := pbc ... y = z := pyz  proves a = z from the given step-wise proofs. = can be replaced with any relation implementing the typeclass Trans. Instead of repeating the right- hand sides, subsequent left-hand sides can be replaced with _.  calc a = b := pab _ = c := pbc ... _ = z := pyz  It is also possible to write the *first* relation as <lhs>\n _ = <rhs> := <proof>. This is useful for aligning relation symbols, especially on longer: identifiers:  calc abc _ = bce := pabce _ = cef := pbcef ... _ = xyz := pwxyz  calc works as a term, as a tactic or as a conv tactic. See [Theorem Proving in Lean 4][tpil4] for more information. [tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α [] ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. _ = ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α [] ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. only [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α]All goals completed! 🐙 _ = [].lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ ysList α.length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) [].length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. All goals completed! 🐙 | case2 xα xsList α ysList α ih(alternate ys xs).length = ys.length + xs.length =>case2αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α) ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length calcStep-wise reasoning over transitive relations.  calc a = b := pab b = c := pbc ... y = z := pyz  proves a = z from the given step-wise proofs. = can be replaced with any relation implementing the typeclass Trans. Instead of repeating the right- hand sides, subsequent left-hand sides can be replaced with _.  calc a = b := pab _ = c := pbc ... _ = z := pyz  It is also possible to write the *first* relation as <lhs>\n _ = <rhs> := <proof>. This is useful for aligning relation symbols, especially on longer: identifiers:  calc abc _ = bce := pabce _ = cef := pbcef ... _ = xyz := pwxyz  calc works as a term, as a tactic or as a conv tactic. See [Theorem Proving in Lean 4][tpil4] for more information. [tpil4]: https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (xα :: xsList α) ysList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. _ = (xα :: alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α) ysList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. only [alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α]All goals completed! 🐙 _ = (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + 1 := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 rflrfl tries to close the current goal using reflexivity. This is supposed to be an extensible tactic and users can add their own support for new reflexive relations. Remark: rfl is an extensible tactic. We later add macro_rules to try different reflexivity theorems (e.g., Iff.rfl). All goals completed! 🐙 _ = (ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. ) + 1 := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ (alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 rwrw is like rewrite, but also tries to close the goal by "cheap" (reducible) rfl afterwards. [ih(alternate ys xs).length = ys.length + xs.length]All goals completed! 🐙 _ = (xα :: xsList α).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. + ysList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (xα :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList α).length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. αType u_1:Type u_1xα:αType u_1xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ysList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih(alternate ys xs).length = ys.length + xs.length:(alternatealternate.{u_1} {α : Type u_1} (xs ys : List α) : List α ysList α xsList α).length = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length⊢ ysList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. ysList α.length; omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙

## Functions with fixed parameters

The above example demonstrates how to use functional induction in the common simple case. We now look into some more tricky aspects of it.

The first arises if the the recursive function has fixed parameters, as in the following function, which cuts up a list into sublists of length at most n:

def cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) : (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) → ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. (ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) | [] => [] | xα::xsList α => (xα::xsList α.takeList.take.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Returns the first n elements of xs, or the whole list if n is too large. * take 0 [a, b, c, d, e] = [] * take 3 [a, b, c, d, e] = [a, b, c] * take 6 [a, b, c, d, e] = [a, b, c, d, e] (nNat-1)) :: cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (xsList α.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (nNat-1)) termination_bySpecify a termination argument for well-founded termination:  termination_by a - b  indicates that termination of the currently defined recursive function follows because the difference between the the arguments a and b. If the fuction takes further argument after the colon, you can name them as follows:  def example (a : Nat) : Nat → Nat → Nat := termination_by b c => a - b  If omitted, a termination argument will be inferred. If written as termination_by?, the inferrred termination argument will be suggested. xsList α => xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. 

Note that in all the recursive calls, the parameter n remains the same, whereas the parameter xs varies. Lean distinguishes between a prefix of fixed parameters, which are identical in all recursive calls, and then the varying parameters, which can change.

Let's first see the function in action:

[["😏", "😄", "😊"], ["😎", "😍", "😘"], ["🥰", "😃", "😉"], ["😋", "😆"]] #eval cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) 3 ["😏""😏" : String, "😄""😄" : String, "😊""😊" : String, "😎""😎" : String, "😍""😍" : String, "😘""😘" : String, "🥰""🥰" : String, "😃""😃" : String, "😉""😉" : String, "😋""😋" : String, "😆""😆" : String]
[["😏", "😄", "😊"], ["😎", "😍", "😘"], ["🥰", "😃", "😉"], ["😋", "😆"]]


Joining these lists back together should give the original list. If we try to use functional induction in the same way as before, we find that we have more goals than we expect. In addition to the expected case1 and case2, we also get a subgoal called n:

theorem cutNJoin1cutNJoin1.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : (cutN n xs).join = xs (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : List.joinList.join.{u} {α : Type u} : List (List α) → List αO(|join L|). join L concatenates all the lists in L into one list. * join [[a], [], [b, c], [d, e, f]] = [a, b, c, d, e, f] (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat xsList α) = xsList α := unsolved goals case n α : Type u_1 n : Nat ⊢ Nat case case1 α : Type u_1 n : Nat ⊢ (cutN n []).join = [] case case2 α : Type u_1 n : Nat x✝ : α xs✝ : List α ih1✝ : (cutN n (List.drop (?n - 1) xs✝)).join = List.drop (?n - 1) xs✝ ⊢ (cutN n (x✝ :: xs✝)).join = x✝ :: xs✝byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat xsList α).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList α using cutN.inductcutN.induct.{u_1} {α : Type u_1} (n : Nat) (motive : List α → Prop) (case1 : motive []) (case2 : ∀ (x : α) (xs : List α), motive (List.drop (n - 1) xs) → motive (x :: xs)) (xs : List α) : motive xsnαType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ⊢ NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). case1αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat []).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) []case2αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). x✝α:αType u_1xs✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih1✝(cutN n (List.drop (?n - 1) xs✝)).join = List.drop (?n - 1) xs✝:(cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (?n - HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γa - b computes the difference of a and b. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: a - b = 0 when a ≤ b. 1) xs✝List α)).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (?n - HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γa - b computes the difference of a and b. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: a - b = 0 when a ≤ b. 1) xs✝List α⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α)).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α
unsolved goals
case n
α : Type u_1
n : Nat
⊢ Nat

case case1
α : Type u_1
n : Nat
⊢ (cutN n []).join = []

case case2
α : Type u_1
n : Nat
x✝ : α
xs✝ : List α
ih1✝ : (cutN n (List.drop (?n - 1) xs✝)).join = List.drop (?n - 1) xs✝
⊢ (cutN n (x✝ :: xs✝)).join = x✝ :: xs✝


This is not really a “case”, but the induction tactic does not know that. If we look at the type of cutN.induct, we see that – just like cutN itself - it has a parameter called n, which is hardly surprising:

cutN.inductcutN.induct.{u_1} {α : Type u_1} (n : Nat) (motive : List α → Prop) (case1 : motive []) (case2 : ∀ (x : α) (xs : List α), motive (List.drop (n - 1) xs) → motive (x :: xs)) (xs : List α) : motive xs {αType u_1 : TypeA type universe. Type ≡ Type 0, Type u ≡ Sort (u + 1). _} (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (motiveList α → Prop : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 → Prop) (case1motive [] : motiveList α → Prop []) (case2∀ (x : α) (xs : List α), motive (List.drop (n - 1) xs) → motive (x :: xs) : ∀ (xα : αType u_1) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1), motiveList α → Prop (List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (nNat - 1) xsList α) → motiveList α → Prop (xα :: xsList α)) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : motiveList α → Prop xsList α

Note that the motive depends only on the varying parameter of cutN, namely xs, whereas the fixed parameter of cutN, namely n, did not turn into a target of the induction principle, but merely a regular parameter.

So because n is not a target, we cannot write induction n, xs. Instead, we have to instantiate this parameter in the expression passed to using, and now the proof turns out rather nice:

theorem cutNJoin2cutNJoin2.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : (cutN n xs).join = xs (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : List.joinList.join.{u} {α : Type u} : List (List α) → List αO(|join L|). join L concatenates all the lists in L into one list. * join [[a], [], [b, c], [d, e, f]] = [a, b, c, d, e, f] (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat xsList α) = xsList α := byby tac constructs a term of the expected type by running the tactic(s) tac. αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). xsList α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat xsList α).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) xsList α inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList α using cutN.inductcutN.induct.{u_1} {α : Type u_1} (n : Nat) (motive : List α → Prop) (case1 : motive []) (case2 : ∀ (x : α) (xs : List α), motive (List.drop (n - 1) xs) → motive (x :: xs)) (xs : List α) : motive xs nNatcase1αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat []).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) []case2αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). x✝α:αType u_1xs✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih1✝(cutN n (List.drop (n - 1) xs✝)).join = List.drop (n - 1) xs✝:(cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (nNat - HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γa - b computes the difference of a and b. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: a - b = 0 when a ≤ b. 1) xs✝List α)).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (nNat - HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γa - b computes the difference of a and b. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: a - b = 0 when a ≤ b. 1) xs✝List α⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α)).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α <;>case1αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat []).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) []case2αType u_1:Type u_1nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). x✝α:αType u_1xs✝List α:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1ih1✝(cutN n (List.drop (n - 1) xs✝)).join = List.drop (n - 1) xs✝:(cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (nNat - HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γa - b computes the difference of a and b. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: a - b = 0 when a ≤ b. 1) xs✝List α)).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] (nNat - HSub.hSub.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HSub α β γ] : α → β → γa - b computes the difference of a and b. The meaning of this notation is type-dependent. * For natural numbers, this operator saturates at 0: a - b = 0 when a ≤ b. 1) xs✝List α⊢ (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat (x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α)).join = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) x✝α :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List α simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α), *]All goals completed! 🐙

We also could have used the syntax cutN.induct (n := n) if we prefer named arguments.

## Overlapping patterns

So far we only defined functions with non-overlapping patterns. Lean also supports defining functions with overlapping patterns, and here functional induction can be particularly helpful.

Our (contrived) example function is one that removes repeated elements from a list of Boolean values:

def destutter_root_.destutter (xs : List Bool) : List Bool : (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) → ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. | [] => [] | trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xsList Bool => destutter_root_.destutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xsList Bool) | falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool => destutter_root_.destutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool) | xBool :: xsList Bool => xBool :: destutter_root_.destutter (xs : List Bool) : List Bool xsList Bool

The first three patterns of the function definition do not overlap, so they can be read as theorems that hold unconditionally. Lean generates such theorems, for use with rw and simp:

destutter.eq_3destutter.eq_3 (xs : List Bool) : destutter (false :: false :: xs) = destutter (false :: xs) (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) : destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool) = destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool)

But the fourth equation is not a theorem. In general, it is not true that destutter (x :: xs) = x :: destutter xs holds. This equation is only true when the previous cases did not apply. Lean expresses this by preconditions on the equational lemma:

destutter.eq_4destutter.eq_4 (x_1 : Bool) (xs : List Bool) (x_2 : ∀ (xs_1 : List Bool), x_1 = true → xs = true :: xs_1 → False) (x_3 : ∀ (xs_1 : List Bool), x_1 = false → xs = false :: xs_1 → False) : destutter (x_1 :: xs) = x_1 :: destutter xs (x_1Bool : BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) (x_2∀ (xs_1 : List Bool), x_1 = true → xs = true :: xs_1 → False : ∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x_1Bool = trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → xsList Bool = trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) ) (x_3∀ (xs_1 : List Bool), x_1 = false → xs = false :: xs_1 → False : ∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x_1Bool = falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. → xsList Bool = falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) ) : destutterdestutter (xs : List Bool) : List Bool (x_1Bool :: xsList Bool) = x_1Bool :: destutterdestutter (xs : List Bool) : List Bool xsList Bool

One rather annoying consequence is that if we try to construct a proof about destutter by repeating the same pattern matches, we cannot make progress:

theorem length_destutter_root_.length_destutter (xs : List Bool) : (destutter xs).length ≤ xs.length : (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) → (destutterdestutter (xs : List Bool) : List Bool xsList Bool).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. ≤ xsList Bool.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. | [] => byby tac constructs a term of the expected type by running the tactic(s) tac. ⊢ (destutterdestutter (xs : List Bool) : List Bool []).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y [].length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [destutterdestutter (xs : List Bool) : List Bool]All goals completed! 🐙 | trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xsList Bool => byby tac constructs a term of the expected type by running the tactic(s) tac. xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ⊢ (destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length haveThe have tactic is for adding hypotheses to the local context of the main goal. * have h : t := e adds the hypothesis h : t if e is a term of type t. * have h := e uses the type of e for t. * have : t := e and have := e use this for the name of the hypothesis. * have pat := e for a pattern pat is equivalent to match e with | pat => _, where _ stands for the tactics that follow this one. It is convenient for types that have only one applicable constructor. For example, given h : p ∧ q ∧ r, have ⟨h₁, h₂, h₃⟩ := h produces the hypotheses h₁ : p, h₂ : q, and h₃ : r. := length_destutter_root_.length_destutter (xs : List Bool) : (destutter xs).length ≤ xs.length (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xsList Bool)xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. this(destutter (true :: xs)).length ≤ (true :: xs).length:(destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length⊢ (destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length; simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [destutterdestutter (xs : List Bool) : List Bool] atLocation specifications are used by many tactics that can operate on either the hypotheses or the goal. It can have one of the forms: * 'empty' is not actually present in this syntax, but most tactics use (location)? matchers. It means to target the goal only. * at h₁ ... hₙ: target the hypotheses h₁, ..., hₙ * at h₁ h₂ ⊢: target the hypotheses h₁ and h₂, and the goal * at *: target all hypotheses and the goal *xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. this(destutter (true :: xs)).length ≤ xs.length + 1:(destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xsList Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1⊢ (destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xsList Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1; omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙 | falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool => byby tac constructs a term of the expected type by running the tactic(s) tac. xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ⊢ (destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length haveThe have tactic is for adding hypotheses to the local context of the main goal. * have h : t := e adds the hypothesis h : t if e is a term of type t. * have h := e uses the type of e for t. * have : t := e and have := e use this for the name of the hypothesis. * have pat := e for a pattern pat is equivalent to match e with | pat => _, where _ stands for the tactics that follow this one. It is convenient for types that have only one applicable constructor. For example, given h : p ∧ q ∧ r, have ⟨h₁, h₂, h₃⟩ := h produces the hypotheses h₁ : p, h₂ : q, and h₃ : r. := length_destutter_root_.length_destutter (xs : List Bool) : (destutter xs).length ≤ xs.length (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool)xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. this(destutter (false :: xs)).length ≤ (false :: xs).length:(destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length⊢ (destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length; simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [destutterdestutter (xs : List Bool) : List Bool] atLocation specifications are used by many tactics that can operate on either the hypotheses or the goal. It can have one of the forms: * 'empty' is not actually present in this syntax, but most tactics use (location)? matchers. It means to target the goal only. * at h₁ ... hₙ: target the hypotheses h₁, ..., hₙ * at h₁ h₂ ⊢: target the hypotheses h₁ and h₂, and the goal * at *: target all hypotheses and the goal *xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. this(destutter (false :: xs)).length ≤ xs.length + 1:(destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xsList Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1⊢ (destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xsList Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1; omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙 | xBool :: xsList Bool => byby tac constructs a term of the expected type by running the tactic(s) tac. xBool:BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ⊢ (destutterdestutter (xs : List Bool) : List Bool (xBool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (xBool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length simp made no progresssimpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. only [destutterdestutter (xs : List Bool) : List Bool]xBool:BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ⊢ (destutterdestutter (xs : List Bool) : List Bool (xBool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (xBool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xsList Bool).length -- simp made no progress

In the last case of the match, Lean does not know that the previous cases did not apply, so it cannot use destutter.eq_4 and thus cannot simplify the goal.

One way out is to resort to the unfold destutter; split idiom. Functional induction also covers this case. The induction theorem produced by Lean provides the assumptions needed by the equational theorem in the appropriate case:

destutter.inductdestutter.induct (motive : List Bool → Prop) (case1 : motive []) (case2 : ∀ (xs : List Bool), motive (true :: xs) → motive (true :: true :: xs)) (case3 : ∀ (xs : List Bool), motive (false :: xs) → motive (false :: false :: xs)) (case4 : ∀ (x : Bool) (xs : List Bool), (∀ (xs_1 : List Bool), x = true → xs = true :: xs_1 → False) → (∀ (xs_1 : List Bool), x = false → xs = false :: xs_1 → False) → motive xs → motive (x :: xs)) (xs : List Bool) : motive xs (motiveList Bool → Prop : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. → Prop) (case1motive [] : motiveList Bool → Prop []) (case2∀ (xs : List Bool), motive (true :: xs) → motive (true :: true :: xs) : ∀ (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), motiveList Bool → Prop (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xsList Bool) → motiveList Bool → Prop (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xsList Bool)) (case3∀ (xs : List Bool), motive (false :: xs) → motive (false :: false :: xs) : ∀ (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), motiveList Bool → Prop (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool) → motiveList Bool → Prop (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xsList Bool)) (case4∀ (x : Bool) (xs : List Bool), (∀ (xs_1 : List Bool), x = true → xs = true :: xs_1 → False) → (∀ (xs_1 : List Bool), x = false → xs = false :: xs_1 → False) → motive xs → motive (x :: xs) : ∀ (xBool : BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), (∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), xBool = trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → xsList Bool = trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) ) → (∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), xBool = falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. → xsList Bool = falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) ) → motiveList Bool → Prop xsList Bool → motiveList Bool → Prop (xBool :: xsList Bool)) (xsList Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ) : motiveList Bool → Prop xsList Bool

With these assumptions in the context, simp makes progress, and the proof becomes quite short:

theorem length_destutter2_root_.length_destutter2 {xs : List Bool} : (destutter xs).length ≤ xs.length : (destutterdestutter (xs : List Bool) : List Bool xsList Bool).lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. ≤ xsList Bool.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. := byby tac constructs a term of the expected type by running the tactic(s) tac. xsList Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ⊢ (destutterdestutter (xs : List Bool) : List Bool xsList Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xsList Bool.length inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList Bool using destutter.inductdestutter.induct (motive : List Bool → Prop) (case1 : motive []) (case2 : ∀ (xs : List Bool), motive (true :: xs) → motive (true :: true :: xs)) (case3 : ∀ (xs : List Bool), motive (false :: xs) → motive (false :: false :: xs)) (case4 : ∀ (x : Bool) (xs : List Bool), (∀ (xs_1 : List Bool), x = true → xs = true :: xs_1 → False) → (∀ (xs_1 : List Bool), x = false → xs = false :: xs_1 → False) → motive xs → motive (x :: xs)) (xs : List Bool) : motive xscase1⊢ (destutterdestutter (xs : List Bool) : List Bool []).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y [].lengthcase2xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter (true :: xs✝)).length ≤ (true :: xs✝).length:(destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).length⊢ (destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).lengthcase3xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter (false :: xs✝)).length ≤ (false :: xs✝).length:(destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).length⊢ (destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).lengthcase4x✝²Bool:BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. x✝¹∀ (xs_1 : List Bool), x✝² = true → xs✝ = true :: xs_1 → False:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) x✝∀ (xs_1 : List Bool), x✝² = false → xs✝ = false :: xs_1 → False:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. → xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) ih1✝(destutter xs✝).length ≤ xs✝.length:(destutterdestutter (xs : List Bool) : List Bool xs✝List Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length⊢ (destutterdestutter (xs : List Bool) : List Bool (x✝²Bool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (x✝²Bool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).length <;>case1⊢ (destutterdestutter (xs : List Bool) : List Bool []).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y [].lengthcase2xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter (true :: xs✝)).length ≤ (true :: xs✝).length:(destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).length⊢ (destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).lengthcase3xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter (false :: xs✝)).length ≤ (false :: xs✝).length:(destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).length⊢ (destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).lengthcase4x✝²Bool:BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. x✝¹∀ (xs_1 : List Bool), x✝² = true → xs✝ = true :: xs_1 → False:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) x✝∀ (xs_1 : List Bool), x✝² = false → xs✝ = false :: xs_1 → False:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. → xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Bool → FalseFalse : PropFalse is the empty proposition. Thus, it has no introduction rules. It represents a contradiction. False elimination rule, False.rec, expresses the fact that anything follows from a contradiction. This rule is sometimes called ex falso (short for ex falso sequitur quodlibet), or the principle of explosion. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) ih1✝(destutter xs✝).length ≤ xs✝.length:(destutterdestutter (xs : List Bool) : List Bool xs✝List Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length⊢ (destutterdestutter (xs : List Bool) : List Bool (x✝²Bool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y (x✝²Bool :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool).length simpThe simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants: - simp simplifies the main goal target using lemmas tagged with the attribute [simp]. - simp [h₁, h₂, ..., hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f. - simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses. - simp only [h₁, h₂, ..., hₙ] is like simp [h₁, h₂, ..., hₙ] but does not use [simp] lemmas. - simp [-id₁, ..., -idₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ. - simp at h₁ h₂ ... hₙ simplifies the hypotheses h₁ : T₁ ... hₙ : Tₙ. If the target or another hypothesis depends on hᵢ, a new simplified hypothesis hᵢ is introduced, but the old one remains in the local context. - simp at * simplifies all the hypotheses and the target. - simp [*] at * simplifies target and all (propositional) hypotheses using the other hypotheses. [destutterdestutter (xs : List Bool) : List Bool] atLocation specifications are used by many tactics that can operate on either the hypotheses or the goal. It can have one of the forms: * 'empty' is not actually present in this syntax, but most tactics use (location)? matchers. It means to target the goal only. * at h₁ ... hₙ: target the hypotheses h₁, ..., hₙ * at h₁ h₂ ⊢: target the hypotheses h₁ and h₂, and the goal * at *: target all hypotheses and the goal *case4x✝²Bool:BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter xs✝).length ≤ xs✝.length:(destutterdestutter (xs : List Bool) : List Bool xs✝List Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.lengthx✝¹∀ (xs_1 : List Bool), x✝² = true → ¬xs✝ = true :: xs_1:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → ¬Not (a : Prop) : PropNot p, or ¬p, is the negation of p. It is defined to be p → False, so if your goal is ¬p you can use intro h to turn the goal into h : p ⊢ False, and if you have hn : ¬p and h : p then hn h : False and (hn h).elim will prove anything. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Boolx✝∀ (xs_1 : List Bool), x✝² = false → ¬xs✝ = false :: xs_1:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. → ¬Not (a : Prop) : PropNot p, or ¬p, is the negation of p. It is defined to be p → False, so if your goal is ¬p you can use intro h to turn the goal into h : p ⊢ False, and if you have hn : ¬p and h : p then hn h : False and (hn h).elim will prove anything. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Bool⊢ (destutterdestutter (xs : List Bool) : List Bool xs✝List Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length <;>case2xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter (true :: xs✝)).length ≤ xs✝.length + 1:(destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1⊢ (destutterdestutter (xs : List Bool) : List Bool (trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1case3xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter (false :: xs✝)).length ≤ xs✝.length + 1:(destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1⊢ (destutterdestutter (xs : List Bool) : List Bool (falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs✝List Bool)).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1case4x✝²Bool:BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. xs✝List Bool:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ih1✝(destutter xs✝).length ≤ xs✝.length:(destutterdestutter (xs : List Bool) : List Bool xs✝List Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.lengthx✝¹∀ (xs_1 : List Bool), x✝² = true → ¬xs✝ = true :: xs_1:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → ¬Not (a : Prop) : PropNot p, or ¬p, is the negation of p. It is defined to be p → False, so if your goal is ¬p you can use intro h to turn the goal into h : p ⊢ False, and if you have hn : ¬p and h : p then hn h : False and (hn h).elim will prove anything. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Boolx✝∀ (xs_1 : List Bool), x✝² = false → ¬xs✝ = false :: xs_1:∀ (xs_1List Bool : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. ), x✝²Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. → ¬Not (a : Prop) : PropNot p, or ¬p, is the negation of p. It is defined to be p → False, so if your goal is ¬p you can use intro h to turn the goal into h : p ⊢ False, and if you have hn : ¬p and h : p then hn h : False and (hn h).elim will prove anything. For more information: [Propositional Logic](https://lean-lang.org/theorem_proving_in_lean4/propositions_and_proofs.html#propositional-logic) xs✝List Bool = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xs_1List Bool⊢ (destutterdestutter (xs : List Bool) : List Bool xs✝List Bool).length ≤ LE.le.{u} {α : Type u} [self : LE α] : α → α → PropThe less-equal relation: x ≤ y xs✝List Bool.length omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙

You might be surprised that simp [destutter] suffices here, and we did not have to explicitly write simp [destutter, *] to tell simp that it should also use facts in the local context. This is due to special handling in simp: if rewrite rules have premises that look like like the premise of a function’s equational lemma, simp will consult the local context anyways.

This special handling does not exist in rewrite and rw, so if we use these tactics to unfold the function, we will get new goals with these side conditions, and have to resolve them explicitly, for example using assumption.

## Mutual recursion

Proofs about mutually defined functions can become quite tricky: following the rule that the proof should follow the structure of the function, the usual approach is to prove two mutually recursive theorems, replicating the case splits of the original functions by matching or using split. Again, functional induction can help us avoid this repetition.

We will use a minimal example of mutually defined functions, even and odd:

mutual def even_root_.even (n : Nat) : Bool : (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. | 0 => trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. | nNat+1 => odd_root_.odd (n : Nat) : Bool nNat def odd_root_.odd (n : Nat) : Bool : (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. | 0 => falseBool.false : BoolThe boolean value false, not to be confused with the proposition False. | nNat+1 => even_root_.even (n : Nat) : Bool nNat end

The functional induction principle generated for one of these functions looks as follows:

even.inducteven.induct (motive1 motive2 : Nat → Prop) (case1 : motive1 0) (case2 : ∀ (n : Nat), motive2 n → motive1 n.succ) (case3 : motive2 0) (case4 : ∀ (n : Nat), motive1 n → motive2 n.succ) (n : Nat) : motive1 n (motive1Nat → Prop motive2Nat → Prop : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). → Prop) (case1motive1 0 : motive1Nat → Prop 0) (case2∀ (n : Nat), motive2 n → motive1 n.succ : ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), motive2Nat → Prop nNat → motive1Nat → Prop nNat.succNat.succ (n : Nat) : NatThe successor function on natural numbers, succ n = n + 1. This is one of the two constructors of Nat. ) (case3motive2 0 : motive2Nat → Prop 0) (case4∀ (n : Nat), motive1 n → motive2 n.succ : ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), motive1Nat → Prop nNat → motive2Nat → Prop nNat.succNat.succ (n : Nat) : NatThe successor function on natural numbers, succ n = n + 1. This is one of the two constructors of Nat. ) (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) : motive1Nat → Prop nNat

This theorem now has two motives, one for each function in the recursive group, and four cases, corresponding to the two cases of even and then two cases of odd.

Recall that the role of the motive is to specify the statement that we want to prove about the parameters of the recursive function. If we have two mutually recursive functions, we typically have two such statements in mind that we want to prove together, one for each of the two functions.

One of the main benefits of the induction tactic, over simply trying to apply an induction principle, is that the induction tactic constructs the motive from the current proof goal. If the proof goal is P x y then induction x, y sets (motive := fun x y => P x y). (There is a bit more to it when there are assumptions about x or y in the local context, or the generalizing clause is used.)

So if the induction principle expects multiple motives, only of them can be inferred from the goal, and we will have to instantiate the other motive explicitly. So to prove that all numbers of the form 2n are even, were we want to use that all numbers of the form 2n + 1 are odd, we can write:

theorem evenDouble_root_.evenDouble (n : Nat) : even (2 * n) = true (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) : eveneven (n : Nat) : Bool (2 * nNat) := byby tac constructs a term of the expected type by running the tactic(s) tac. nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. nNat using even.inducteven.induct (motive1 motive2 : Nat → Prop) (case1 : motive1 0) (case2 : ∀ (n : Nat), motive2 n → motive1 n.succ) (case3 : motive2 0) (case4 : ∀ (n : Nat), motive1 n → motive2 n.succ) (n : Nat) : motive1 n (motive2 := fun nNat => oddodd (n : Nat) : Bool (2 * nNat + 1))case1⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case2n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝odd (2 * n✝ + 1) = true:oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case3⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case4n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝even (2 * n✝) = true:eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. <;>case1⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case2n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝odd (2 * n✝ + 1) = true:oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case3⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case4n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝even (2 * n✝) = true:eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. simp_allsimp_all is a stronger version of simp [*] at * where the hypotheses and target are simplified multiple times until no simplification is applicable. Only non-dependent propositional hypotheses are considered. [eveneven (n : Nat) : Bool, oddodd (n : Nat) : Bool, Nat.mul_addNat.mul_add (n m k : Nat) : n * (m + k) = n * m + n * k]All goals completed! 🐙

We can of course also prove the statement about odd. Now the induction tactic infers motive2 and we have to give motive1 explicitly:

theorem oddDouble_root_.oddDouble (n : Nat) : odd (2 * n + 1) = true (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) : oddodd (n : Nat) : Bool (2 * nNat + 1) := byby tac constructs a term of the expected type by running the tactic(s) tac. nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. nNat using odd.inductodd.induct (motive1 motive2 : Nat → Prop) (case1 : motive1 0) (case2 : ∀ (n : Nat), motive2 n → motive1 n.succ) (case3 : motive2 0) (case4 : ∀ (n : Nat), motive1 n → motive2 n.succ) (n : Nat) : motive2 n (motive1 := fun nNat => eveneven (n : Nat) : Bool (2 * nNat))case1⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case2n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝odd (2 * n✝ + 1) = true:oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case3⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case4n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝even (2 * n✝) = true:eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. <;>case1⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case2n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝odd (2 * n✝ + 1) = true:oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case3⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case4n✝Nat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ih1✝even (2 * n✝) = true:eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. n✝Nat.succ + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. simp_allsimp_all is a stronger version of simp [*] at * where the hypotheses and target are simplified multiple times until no simplification is applicable. Only non-dependent propositional hypotheses are considered. [eveneven (n : Nat) : Bool, oddodd (n : Nat) : Bool, Nat.mul_addNat.mul_add (n m k : Nat) : n * (m + k) = n * m + n * k]All goals completed! 🐙

If you compare even.induct and odd.induct, or inspect the proof state after induction, you will notice that we get the same proof obligations in both proofs, and we had to perform the same proofs twice to get both theorems. In the example above, where the proofs are rather trivial, this is not so bad, but in general we would be quite unsatisfied by that repetition. In that case, we can use the mutual functional induction principle:

even.mutual_inducteven.mutual_induct (motive1 motive2 : Nat → Prop) (case1 : motive1 0) (case2 : ∀ (n : Nat), motive2 n → motive1 n.succ) (case3 : motive2 0) (case4 : ∀ (n : Nat), motive1 n → motive2 n.succ) : (∀ (n : Nat), motive1 n) ∧ ∀ (n : Nat), motive2 n (motive1Nat → Prop motive2Nat → Prop : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). → Prop) (case1motive1 0 : motive1Nat → Prop 0) (case2∀ (n : Nat), motive2 n → motive1 n.succ : ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), motive2Nat → Prop nNat → motive1Nat → Prop nNat.succNat.succ (n : Nat) : NatThe successor function on natural numbers, succ n = n + 1. This is one of the two constructors of Nat. ) (case3motive2 0 : motive2Nat → Prop 0) (case4∀ (n : Nat), motive1 n → motive2 n.succ : ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), motive1Nat → Prop nNat → motive2Nat → Prop nNat.succNat.succ (n : Nat) : NatThe successor function on natural numbers, succ n = n + 1. This is one of the two constructors of Nat. ) : (∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), motive1Nat → Prop nNat) ∧ (∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), motive2Nat → Prop nNat)

This induction principle proves both motive1 and motive2. Unfortunately, this kind of induction principle can no longer be used by induction, so we have to manually prepare the goal to be of the right shape to be able to apply the theorem, essentially constructing both motives manually, and then project out the components:

theorem evenDouble_oddDouble_root_.evenDouble_oddDouble : (∀ (n : Nat), even (2 * n) = true) ∧ ∀ (n : Nat), odd (2 * n + 1) = true : ((nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → eveneven (n : Nat) : Bool (2 * nNat)) ∧ ((nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → oddodd (n : Nat) : Bool (2 * nNat + 1)) := byby tac constructs a term of the expected type by running the tactic(s) tac. ⊢ (∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. ) ∧ And (a b : Prop) : PropAnd a b, or a ∧ b, is the conjunction of propositions. It can be constructed and destructed like a pair: if ha : a and hb : b then ⟨ha, hb⟩ : a ∧ b, and if h : a ∧ b then h.left : a and h.right : b. ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. applyapply e tries to match the current goal against the conclusion of e's type. If it succeeds, then the tactic returns as many subgoals as the number of premises that have not been fixed by type inference or type class resolution. Non-dependent premises are added before dependent ones. The apply tactic uses higher-order pattern matching, type class resolution, and first-order unification with dependent types. even.mutual_inducteven.mutual_induct (motive1 motive2 : Nat → Prop) (case1 : motive1 0) (case2 : ∀ (n : Nat), motive2 n → motive1 n.succ) (case3 : motive2 0) (case4 : ∀ (n : Nat), motive1 n → motive2 n.succ) : (∀ (n : Nat), motive1 n) ∧ ∀ (n : Nat), motive2 ncase1⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case2⊢ ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat.succ) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case3⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case4⊢ ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat.succ + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. <;>case1⊢ eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case2⊢ ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat.succ) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case3⊢ oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. 0 + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. case4⊢ ∀ (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ), eveneven (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. → oddodd (n : Nat) : Bool (2 * HMul.hMul.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HMul α β γ] : α → β → γa * b computes the product of a and b. The meaning of this notation is type-dependent. nNat.succ + HAdd.hAdd.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAdd α β γ] : α → β → γa + b computes the sum of a and b. The meaning of this notation is type-dependent. 1) = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. simp_allsimp_all is a stronger version of simp [*] at * where the hypotheses and target are simplified multiple times until no simplification is applicable. Only non-dependent propositional hypotheses are considered. [eveneven (n : Nat) : Bool, oddodd (n : Nat) : Bool, Nat.mul_addNat.mul_add (n m k : Nat) : n * (m + k) = n * m + n * k]All goals completed! 🐙 theorem evenDouble2_root_.evenDouble2 (n : Nat) : even (2 * n) = true : (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → eveneven (n : Nat) : Bool (2 * nNat) := evenDouble_oddDoubleevenDouble_oddDouble : (∀ (n : Nat), even (2 * n) = true) ∧ ∀ (n : Nat), odd (2 * n + 1) = true.1 theorem oddDouble2_root_.oddDouble2 (n : Nat) : odd (2 * n + 1) = true : (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → oddodd (n : Nat) : Bool (2 * nNat + 1) := evenDouble_oddDoubleevenDouble_oddDouble : (∀ (n : Nat), even (2 * n) = true) ∧ ∀ (n : Nat), odd (2 * n + 1) = true.2

We plan to provide commands to perform such a joint declaration of two theorems with less repetition and more convenience in the future.

Unrelated to mutual induction, but worth pointing out at this point: in this section we were able to solve all subgoals using simp_all [even, odd]. This high level of automation means that our proofs are quite stable under changes to the function definitions, and indeed, if we changed the definition of odd in

mutual def evenAltEvenOdd.even (n : Nat) : Bool : (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) → BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. | 0 => trueBool.true : BoolThe boolean value true, not to be confused with the proposition True. | nNat+1 => oddAltEvenOdd.odd (n : Nat) : Bool nNat def oddAltEvenOdd.odd (n : Nat) : Bool (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) : BoolBool : TypeBool is the type of boolean values, true and false. Classically, this is equivalent to Prop (the type of propositions), but the distinction is important for programming, because values of type Prop are erased in the code generator, while Bool corresponds to the type called bool or boolean in most programming languages. := !(evenAltEvenOdd.even (n : Nat) : Bool nNat) end

all of our proofs still go through, even though the number and shape of the cases changed!

## What is a branch?

When Lean defines the functional induction principle, it has to decide what constitutes a branch of the function, and should therefore become a case of the induction principle. This is necessarily a heuristic, and is guided by the notion of tail position: Lean splits match statements and if-then-else statements that occur in the body of the function or in a case of such statements, but not when they appear, for example, in arguments to functions.

As a slight variant of cutN, consider a function that inserts a separator before, between and after groups of n:

["🐕", "😏", "😄", "😊", "🐕", "😎", "😍", "😘", "🐕", "🥰", "😃", "😉", "🐕", "😋", "😆", "🐕"] #eval sepWithsepWith.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α 3 (byby tac constructs a term of the expected type by running the tactic(s) tac. ⊢ 0 < LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: x < y 3 omegaThe omega tactic, for resolving integer and natural linear arithmetic problems. It is not yet a full decision procedure (no "dark" or "grey" shadows), but should be effective on many problems. We handle hypotheses of the form x = y, x < y, x ≤ y, and k ∣ x for x y in Nat or Int (and k a literal), along with negations of these statements. We decompose the sides of the inequalities as linear combinations of atoms. If we encounter x / k or x % k for literal integers k we introduce new auxiliary variables and the relevant inequalities. On the first pass, we do not perform case splits on natural subtraction. If omega fails, we recursively perform a case split on a natural subtraction appearing in a hypothesis, and try again. The options  omega (config := { splitDisjunctions := true, splitNatSub := true, splitNatAbs := true, splitMinMax := true })  can be used to: * splitDisjunctions: split any disjunctions found in the context, if the problem is not otherwise solvable. * splitNatSub: for each appearance of ((a - b : Nat) : Int), split on a ≤ b if necessary. * splitNatAbs: for each appearance of Int.natAbs a, split on 0 ≤ a if necessary. * splitMinMax: for each occurrence of min a b, split on min a b = a ∨ min a b = b Currently, all of these are on by default. All goals completed! 🐙) "🐕""🐕" : String ["😏""😏" : String, "😄""😄" : String, "😊""😊" : String, "😎""😎" : String, "😍""😍" : String, "😘""😘" : String, "🥰""🥰" : String, "😃""😃" : String, "😉""😉" : String, "😋""😋" : String, "😆""😆" : String]
["🐕", "😏", "😄", "😊", "🐕", "😎", "😍", "😘", "🐕", "🥰", "😃", "😉", "🐕", "😋", "😆", "🐕"]


Instead of using pattern matching as with cutN, let us try to use if-then-else:

def sepWithsepWith.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (hpos0 < n : 0 < nNat) (sepα : αType u_1) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 := ifif c then t else e is notation for ite c t e, "if-then-else", which decides to return t or e depending on whether c is true or false. The explicit argument c : Prop does not have any actual computational content, but there is an additional [Decidable c] argument synthesized by typeclass inference which actually determines how to evaluate c to true or false. Write if h : c then t else e instead for a "dependent if-then-else" dite, which allows t/e to use the fact that c is true/false. xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = 0 thenif c then t else e is notation for ite c t e, "if-then-else", which decides to return t or e depending on whether c is true or false. The explicit argument c : Prop does not have any actual computational content, but there is an additional [Decidable c] argument synthesized by typeclass inference which actually determines how to evaluate c to true or false. Write if h : c then t else e instead for a "dependent if-then-else" dite, which allows t/e to use the fact that c is true/false. [sepα] elseif c then t else e is notation for ite c t e, "if-then-else", which decides to return t or e depending on whether c is true or false. The explicit argument c : Prop does not have any actual computational content, but there is an additional [Decidable c] argument synthesized by typeclass inference which actually determines how to evaluate c to true or false. Write if h : c then t else e instead for a "dependent if-then-else" dite, which allows t/e to use the fact that c is true/false. [sepα] ++ xsList α.takeList.take.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Returns the first n elements of xs, or the whole list if n is too large. * take 0 [a, b, c, d, e] = [] * take 3 [a, b, c, d, e] = [a, b, c] * take 6 [a, b, c, d, e] = [a, b, c, d, e] nNat ++ sepWithsepWith.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α nNat hpos0 < n sepα (xsList α.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] nNat) termination_bySpecify a termination argument for well-founded termination:  termination_by a - b  indicates that termination of the currently defined recursive function follows because the difference between the the arguments a and b. If the fuction takes further argument after the colon, you can name them as follows:  def example (a : Nat) : Nat → Nat → Nat := termination_by b c => a - b  If omitted, a termination argument will be inferred. If written as termination_by?, the inferrred termination argument will be suggested. xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. 

Because we use if-then-else in a tail position the induction principle has two goals, one for each of the two branches:

sepWith.inductsepWith.induct.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (motive : List α → Prop) (case1 : ∀ (x : List α), x.length = 0 → motive x) (case2 : ∀ (x : List α), ¬x.length = 0 → motive (List.drop n x) → motive x) (xs : List α) : motive xs {αType u_1 : TypeA type universe. Type ≡ Type 0, Type u ≡ Sort (u + 1). _} (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (hpos0 < n : 0 < nNat) (sepα : αType u_1) (motiveList α → Prop : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 → Prop) (case1∀ (x : List α), x.length = 0 → motive x : ∀ (xList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1), xList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = 0 → motiveList α → Prop xList α) (case2∀ (x : List α), ¬x.length = 0 → motive (List.drop n x) → motive x : ∀ (xList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1), ¬xList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = 0 → motiveList α → Prop (List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] nNat xList α) → motiveList α → Prop xList α) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : motiveList α → Prop xsList α

We could also have defined the function slightly differently, moving the if-then-else into the (sep :: ·), as follows:

def sepWith2sepWith2.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (hpos0 < n : 0 < nNat) (sepα : αType u_1) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 := sepα :: (ifif c then t else e is notation for ite c t e, "if-then-else", which decides to return t or e depending on whether c is true or false. The explicit argument c : Prop does not have any actual computational content, but there is an additional [Decidable c] argument synthesized by typeclass inference which actually determines how to evaluate c to true or false. Write if h : c then t else e instead for a "dependent if-then-else" dite, which allows t/e to use the fact that c is true/false. xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = 0 thenif c then t else e is notation for ite c t e, "if-then-else", which decides to return t or e depending on whether c is true or false. The explicit argument c : Prop does not have any actual computational content, but there is an additional [Decidable c] argument synthesized by typeclass inference which actually determines how to evaluate c to true or false. Write if h : c then t else e instead for a "dependent if-then-else" dite, which allows t/e to use the fact that c is true/false. [] elseif c then t else e is notation for ite c t e, "if-then-else", which decides to return t or e depending on whether c is true or false. The explicit argument c : Prop does not have any actual computational content, but there is an additional [Decidable c] argument synthesized by typeclass inference which actually determines how to evaluate c to true or false. Write if h : c then t else e instead for a "dependent if-then-else" dite, which allows t/e to use the fact that c is true/false. xsList α.takeList.take.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Returns the first n elements of xs, or the whole list if n is too large. * take 0 [a, b, c, d, e] = [] * take 3 [a, b, c, d, e] = [a, b, c] * take 6 [a, b, c, d, e] = [a, b, c, d, e] nNat ++ sepWith2sepWith2.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α nNat hpos0 < n sepα (xsList α.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] nNat)) termination_bySpecify a termination argument for well-founded termination:  termination_by a - b  indicates that termination of the currently defined recursive function follows because the difference between the the arguments a and b. If the fuction takes further argument after the colon, you can name them as follows:  def example (a : Nat) : Nat → Nat → Nat := termination_by b c => a - b  If omitted, a termination argument will be inferred. If written as termination_by?, the inferrred termination argument will be suggested. xsList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. 

Now we get an induction principle with just one case:

sepWith2.inductsepWith2.induct.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (motive : List α → Prop) (case1 : ∀ (x : List α), (¬x.length = 0 → motive (List.drop n x)) → motive x) (xs : List α) : motive xs {αType u_1 : TypeA type universe. Type ≡ Type 0, Type u ≡ Sort (u + 1). _} (nNat : NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). ) (hpos0 < n : 0 < nNat) (sepα : αType u_1) (motiveList α → Prop : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1 → Prop) (case1∀ (x : List α), (¬x.length = 0 → motive (List.drop n x)) → motive x : ∀ (xList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1), (¬xList α.lengthList.length.{u_1} {α : Type u_1} : List α → NatThe length of a list: [].length = 0 and (a :: l).length = l.length + 1. This function is overridden in the compiler to lengthTR, which uses constant stack space, while leaving this function to use the "naive" recursion which is easier for reasoning. = 0 → motiveList α → Prop (List.dropList.drop.{u} {α : Type u} : Nat → List α → List αO(min n |xs|). Removes the first n elements of xs. * drop 0 [a, b, c, d, e] = [a, b, c, d, e] * drop 3 [a, b, c, d, e] = [d, e] * drop 6 [a, b, c, d, e] = [] nNat xList α)) → motiveList α → Prop xList α) (xsList α : ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. αType u_1) : motiveList α → Prop xsList α

Also note that the induction hypothesis in this case now has a condition ¬x.length = 0. This is to be expected: The corresponding recursive call is in the else-branch, and if x.length = 0 we may not assume that the induction hypothesis holds.

## Hurdle: Looping simp

At this point we should discuss an issue with simp and certain recursive function definitions that can lead to simp [foo] looping. This is relevant because unfolding the function’s definition with simp is often the first thing we do in the case of a functional induction.

Consider the following proof that relates sepWith and cutN

theorem sepWith_cutNsepWith_cutN.{u_1} {n : Nat} {hpos : 0 < n} : ∀ {α : Type u_1} {sep : α} {xs : List α}, sepWith n hpos sep xs = (List.map (fun x => sep :: x) (cutN n xs)).join ++ [sep] : sepWithsepWith.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α nNat hpos0 < n sep?m.43519 xsList ?m.43519 = (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat xsList ?m.43519 |>.mapList.map.{u, v} {α : Type u} {β : Type v} (f : α → β) : List α → List βO(|l|). map f l applies f to each element of the list. * map f [a, b, c] = [f a, f b, f c] (sep?m.43519 :: ·) |>.joinList.join.{u} {α : Type u} : List (List α) → List αO(|join L|). join L concatenates all the lists in L into one list. * join [[a], [], [b, c], [d, e, f]] = [a, b, c, d, e, f] ) ++ [sep?m.43519] := byby tac constructs a term of the expected type by running the tactic(s) tac. nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). hpos0 < n:0 < LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: x < y nNatα✝Type u_1:Type u_1sepα✝:α✝Type u_1xsList α✝:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. When the value is not shared, Array α will have better performance because it can do destructive updates. α✝Type u_1⊢ sepWithsepWith.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α nNat hpos0 < n sepα✝ xsList α✝ = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (List.mapList.map.{u, v} {α : Type u} {β : Type v} (f : α → β) : List α → List βO(|l|). map f l applies f to each element of the list. * map f [a, b, c] = [f a, f b, f c] (fun xList α✝ => sepα✝ :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xList α✝) (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat xsList α✝)).join ++ HAppend.hAppend.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAppend α β γ] : α → β → γa ++ b is the result of concatenation of a and b, usually read "append". The meaning of this notation is type-dependent. [sepα✝] inductionAssuming x is a variable in the local context with an inductive type, induction x applies induction on x to the main goal, producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor and an inductive hypothesis is added for each recursive argument to the constructor. If the type of an element in the local context depends on x, that element is reverted and reintroduced afterward, so that the inductive hypothesis incorporates that hypothesis as well. For example, given n : Nat and a goal with a hypothesis h : P n and target Q n, induction n produces one goal with hypothesis h : P 0 and target Q 0, and one goal with hypotheses h : P (Nat.succ a) and ih₁ : P a → Q a and target Q (Nat.succ a). Here the names a and ih₁ are chosen automatically and are not accessible. You can use with to provide the variables names for each constructor. - induction e, where e is an expression instead of a variable, generalizes e in the goal, and then performs induction on the resulting variable. - induction e using r allows the user to specify the principle of induction that should be used. Here r should be a term whose result type must be of the form C t, where C is a bound variable and t is a (possibly empty) sequence of bound variables - induction e generalizing z₁ ... zₙ, where z₁ ... zₙ are variables in the local context, generalizes over z₁ ... zₙ before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized. - Given x : Nat, induction x with | zero => tac₁ | succ x' ih => tac₂ uses tactic tac₁ for the zero case, and tac₂ for the succ case. xsList α✝ using cutN.inductcutN.induct.{u_1} {α : Type u_1} (n : Nat) (motive : List α → Prop) (case1 : motive []) (case2 : ∀ (x : α) (xs : List α), motive (List.drop (n - 1) xs) → motive (x :: xs)) (xs : List α) : motive xs nNatnNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). hpos0 < n:0 < LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: x < y nNatα✝Type u_1:Type u_1sepα✝:α✝Type u_1⊢ sepWithsepWith.{u_1} {α : Type u_1} (n : Nat) (hpos : 0 < n) (sep : α) (xs : List α) : List α nNat hpos0 < n sepα✝ [] = Eq.{u_1} {α : Sort u_1} : α → α → PropThe equality relation. It has one introduction rule, Eq.refl. We use a = b as notation for Eq a b. A fundamental property of equality is that it is an equivalence relation.  variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd  Equality is much more than an equivalence relation, however. It has the important property that every assertion respects the equivalence, in the sense that we can substitute equal expressions without changing the truth value. That is, given h1 : a = b and h2 : p a, we can construct a proof for p b using substitution: Eq.subst h1 h2. Example:  example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α → Prop) (h1 : a = b) (h2 : p a) : p b := h1 ▸ h2  The triangle in the second presentation is a macro built on top of Eq.subst and Eq.symm, and you can enter it by typing \t. For more information: [Equality](https://lean-lang.org/theorem_proving_in_lean4/quantifiers_and_equality.html#equality) (List.mapList.map.{u, v} {α : Type u} {β : Type v} (f : α → β) : List α → List βO(|l|). map f l applies f to each element of the list. * map f [a, b, c] = [f a, f b, f c] (fun xList α✝ => sepα✝ :: List.cons.{u} {α : Type u} (head : α) (tail : List α) : List αIf a : α and l : List α, then cons a l, or a :: l, is the list whose first element is a and with l as the rest of the list. xList α✝) (cutNcutN.{u_1} {α : Type u_1} (n : Nat) (xs : List α) : List (List α) nNat [])).join ++ HAppend.hAppend.{u, v, w} {α : Type u} {β : Type v} {γ : outParam (Type w)} [self : HAppend α β γ] : α → β → γa ++ b is the result of concatenation of a and b, usually read "append". The meaning of this notation is type-dependent. [sepα✝]case2nNat:NatNat : TypeThe type of natural numbers, starting at zero. It is defined as an inductive type freely generated by "zero is a natural number" and "the successor of a natural number is a natural number". You can prove a theorem P n about n : Nat by induction n, which will expect a proof of the theorem for P 0, and a proof of P (succ i) assuming a proof of P i. The same method also works to define functions by recursion on natural numbers: induction and recursion are two expressions of the same operation from Lean's point of view.  open Nat example (n : Nat) : n < succ n := by induction n with | zero => show 0 < 1 decide | succ i ih => -- ih : i < succ i show succ i < succ (succ i) exact Nat.succ_lt_succ ih  This type is special-cased by both the kernel and the compiler: * The type of expressions contains "Nat literals" as a primitive constructor, and the kernel knows how to reduce zero/succ expressions to nat literals. * If implemented naively, this type would represent a numeral n in unary as a linked list with n links, which is horribly inefficient. Instead, the runtime itself has a special representation for Nat which stores numbers up to 2^63 directly and larger numbers use an arbitrary precision "bignum" library (usually [GMP](https://gmplib.org/)). hpos0 < n:0 < LT.lt.{u} {α : Type u} [self : LT α] : α → α → PropThe less-than relation: x < y nNatα✝Type u_1:Type u_1sepα✝:α✝Type u_1x✝α✝:α✝Type u_1xs✝List α✝:ListList.{u} (α : Type u) : Type uList α is the type of ordered lists with elements of type α. It is implemented as a linked list. List α is isomorphic to Array α, but they are useful for different things: * List α is easier for reasoning, and Array α is modeled as a wrapper around List α * List α works well as a persistent data structure, when many copies of the tail are shared. Wh