Inductive predicates
- Authors:
Yannick Forster, Théo Winterhalter
- Files:
We can define predicates using the Inductive keyword as well.
Inductive even : nat -> Prop :=
| evenO : even 0
| evenS n : even n -> even (S (S n)).Being inductive, their proofs can be constructed using the constructor tactic.
Note: The Goal command is similar to Lemma except it doesn't require a name for a lemma which is useful when just checking things that are not useful later such as the fact the 4 is even…
even 4even 4even 2constructor. Qed.even 0
Case analysis works using inversion.
~ even 3~ even 3h: even 3Falseh: even 3
n: nat
H0: even 1
H: n = 1False
Here we learnt from even 3 that to build it we must have had even 1 first.
inversion H0.
But there is no rule (neither evenO nor evenS) that can unify with even 1 so there is no goal left, we are done!
Qed.inversion is useful but it is hard to predict its behaviour and to name the resulting hypotheses. So we might want to use regular case analysis instead, ie. use destruct.
~ even 3~ even 3h: even 3FalseFalsen: nat
h: even nFalse
Here this is quite terrible because destruct completely forgets about the fact we had 3 so we are left with an unprovable goal.
Abort.There is a way to recover this information by telling Rocq to remember it as an equality using the remember tactic.
~ even 3~ even 3h: even 3Falsen: nat
e: n = 3
h: even nFalsee: 0 = 3Falsen: nat
e: S (S n) = 3
h: even nFalsee: 0 = 3False
See that n has been replaced by 0 but since we also kept the equality e we know this case is impossible.
discriminate.n: nat
e: S (S n) = 3
h: even nFalse
In the other branch, we have that 3 must be 2 + n for some even n which is also impossible but requires noticing that n must be 1. inversion on the equality will give us that.
n: nat
e: S (S n) = 3
h: even n
H0: n = 1False
We can then use subst to automatically substitute n by 1.
h: even 1
e: 3 = 3False
We can now rely on inversion again to discharge the goal.
inversion h. Qed.
Rather than an inductive predicate, we can also define a Boolean function that checks whether its argument is even.
Fixpoint evenb n :=
match n with
| 0 => true
| 1 => false
| S (S n) => evenb n
end.We can show that the two are equivalent.
n: nateven n -> evenb n = truen: nateven n -> evenb n = truen: nat
h: even nevenb n = true
We can perform induction on the proof!
evenb 0 = truen: nat
h: even n
IHh: evenb n = trueevenb (S (S n)) = truereflexivity.evenb 0 = truen: nat
h: even n
IHh: evenb n = trueevenb (S (S n)) = trueassumption. Qed.n: nat
h: even n
IHh: evenb n = trueevenb n = true
And now the other direction.
n: natevenb n = true -> even nn: natevenb n = true -> even nn: nat
h: evenb n = trueeven n
This time we have to perform the induction on n.
h: evenb 0 = trueeven 0n: nat
h: evenb (S n) = true
ih: evenb n = true -> even neven (S n)constructor.h: evenb 0 = trueeven 0n: nat
h: evenb (S n) = true
ih: evenb n = true -> even neven (S n)h: evenb 1 = true
ih: evenb 0 = true -> even 0even 1n: nat
h: evenb (S (S n)) = true
ih: evenb (S n) = true -> even (S n)even (S (S n))h: evenb 1 = true
ih: evenb 0 = true -> even 0even 1discriminate.h: false = true
ih: evenb 0 = true -> even 0even 1n: nat
h: evenb (S (S n)) = true
ih: evenb (S n) = true -> even (S n)even (S (S n))n: nat
h: evenb (S (S n)) = true
ih: evenb (S n) = true -> even (S n)even n
Note how we are stuck here: The inductive hypothesis talks about S n instead of n. But this we cannot generalise.
Abort.The solution is to use a stronger form of induction that lets you conclude about any smaller argument. We'll require Lia which provides the lia tactic (for linear integer arithmetic) to help us deal with some arithmetic goals.
From Stdlib Require Import Lia.In the induction principle below we use forall which is used to quantify over anything from natural numbers to propositions. We can use intros to introduce those variables, the same way we do it for implication.
The strong induction principle is as you'd expect: to prove some property P about n, you can assume it holds for all natural numbers that are smaller.
forall P : nat -> Prop, (forall n : nat, (forall m : nat, m < n -> P m) -> P n) -> forall n : nat, P nforall P : nat -> Prop, (forall n : nat, (forall m : nat, m < n -> P m) -> P n) -> forall n : nat, P nP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: natP n
We'll show a stronger property by induction, that P is valid for n and everything below.
P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: natforall m : nat, m <= n -> P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
hn: forall m : nat, m <= n -> P mP nP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: natforall m : nat, m <= n -> P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P nforall m : nat, m <= 0 -> P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P mforall m : nat, m <= S n -> P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P nforall m : nat, m <= 0 -> P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
m: nat
hm: m <= 0P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
m: nat
hm: m <= 0
H: m = 0P 0P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
m: nat
hm: m <= 0
H: m = 0forall m0 : nat, m0 < 0 -> P m0lia.P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
m: nat
hm: m <= 0
H: m = 0
k: nat
hk: k < 0P k
lia knows there can't be k < 0 so it solves the goal for us. It is implicitly using a proof of ~ k < 0 together with False elimination.
P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P mforall m : nat, m <= S n -> P mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S nP mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
H: m = S nP (S n)P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
m0: nat
H0: m <= n
H: m0 = nP mP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
H: m = S nP (S n)P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
H: m = S nforall m0 : nat, m0 < S n -> P m0P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
H: m = S n
k: nat
hk: k < S nP klia.P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
H: m = S n
k: nat
hk: k < S nk <= nP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
m0: nat
H0: m <= n
H: m0 = nP mlia.P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
ih: forall m : nat, m <= n -> P m
m: nat
hm: m <= S n
m0: nat
H0: m <= n
H: m0 = nm <= nP: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
hn: forall m : nat, m <= n -> P mP nlia. Qed.P: nat -> Prop
h: forall n : nat, (forall m : nat, m < n -> P m) -> P n
n: nat
hn: forall m : nat, m <= n -> P mn <= n
Back to even. We can now use our stronger induction principle thanks to the using clause of the induction tactic. Indeed, the tactic can use anything that looks like an induction principle (which is admittedly a vague notion) which can be very handy!
n: natevenb n = true -> even nn: natevenb n = true -> even nn: nat
h: evenb n = trueeven nn: nat
ih: forall m : nat, m < n -> evenb m = true -> even m
h: evenb n = trueeven n
As opposed to regular induction, we only have one case to consider: an arbitrary
n but for which we have an induction hypothesis.
We still need to perform case analysis, so we'll do it manually.
We have to consider the cases 0, 1 and S (S n).
We could perform destruct n once to get cases 0 and S n and then in
the second branch do destruct again, but we can do it all at once by using
square brackets within square brackets.
ih: forall m : nat, m < 0 -> evenb m = true -> even m
h: evenb 0 = trueeven 0ih: forall m : nat, m < 1 -> evenb m = true -> even m
h: evenb 1 = trueeven 1n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb (S (S n)) = trueeven (S (S n))constructor.ih: forall m : nat, m < 0 -> evenb m = true -> even m
h: evenb 0 = trueeven 0ih: forall m : nat, m < 1 -> evenb m = true -> even m
h: evenb 1 = trueeven 1discriminate.ih: forall m : nat, m < 1 -> evenb m = true -> even m
h: false = trueeven 1n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb (S (S n)) = trueeven (S (S n))n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb n = trueeven (S (S n))n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb n = trueeven nn: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb n = truen < S (S n)n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb n = trueevenb n = truelia.n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb n = truen < S (S n)assumption. Qed.n: nat
ih: forall m : nat, m < S (S n) -> evenb m = true -> even m
h: evenb n = trueevenb n = true
Exercise: Strong induction
We now allow you to use lia.
Prove strong induction yourself, without looking at the proof above.
Module StrongInd.forall P : nat -> Prop, (forall n : nat, (forall m : nat, m < n -> P m) -> P n) -> forall n : nat, P nAdmitted. End StrongInd.forall P : nat -> Prop, (forall n : nat, (forall m : nat, m < n -> P m) -> P n) -> forall n : nat, P n
Exercise: Evenness
Here are two definitions of even numbers.
Definition even1 n := exists m, n = 2 * m. Fixpoint even2 (n : nat) := match n with | 0 => true | 1 => false | S (S n) => even2 n end.
Define an even predicate similar to how you would define a Boolean predicate but using propositions instead.
And a fourth one, as an inductive predicate.
Inductive even4 : nat -> Prop :=
| even4O : even4 0
| even4S n : even4 n -> even4 (S (S n)).Spell out the induction principle of even4 without checking it.
⚠️ Do not forget this one.
Hint: If you want to assert an equality (e : a = b) and then use
rewrite e, then you can also make use of the replace tactic.
replace a with b will replace a with b in the goal and ask you to prove
the equality between the two.
n: nateven1 n -> even2 n = trueAdmitted.n: nateven1 n -> even2 n = truen: nateven2 n = true <-> even3 nAdmitted.n: nateven2 n = true <-> even3 n
Hint: The tactic contradiction is useful if you have an obviously false
hypothesis.
n: nateven3 n -> even4 nAdmitted.n: nateven3 n -> even4 nn: nateven4 n -> even1 nAdmitted.n: nateven4 n -> even1 n
The "less than or equal" predicate <= we have used above is defined as follows.
The idea is that n ≤ n always holds, and if n ≤ m then n ≤ S m. In other words, if you can remove successors S from m and reach n then n ≤ m.
Inductive le : nat -> nat -> Prop :=
| leO n : le n n
| leS n m : le n m -> le n (S m).We build the proof as hinted above, by removing S from 5 until we reach 3 which takes only two steps.
le 3 5le 3 5le 3 4constructor. Qed.le 3 3
To show that 1 is not below 0 we can use inversion again.
~ le 1 0~ le 1 0inversion h. Qed.h: le 1 0False
As for even we can also do it more manually.
~ le 1 0~ le 1 0h: le 1 0Falsen1: nat
e1: n1 = 1
h: le n1 0Falsen1, n0: nat
e0: n0 = 0
e1: n1 = S n0
h: le n1 n0Falsen: nat
e0: n = 0
e1: n = S nFalsem: nat
e0: S m = 0
n: nat
e1: n = S (S m)
h: le n mFalsen: nat
e0: n = 0
e1: n = S nFalsediscriminate.e1: 0 = 1Falsem: nat
e0: S m = 0
n: nat
e1: n = S (S m)
h: le n mFalsediscriminate. Qed.m: nat
e0: S m = 0
h: le (S (S m)) mFalse
As a sanity check, we can verify the following property.
Below exists (x : A), P x is very similar to sigT P except that P x is a proposition. We can prove exists x, P x by e.g., using exists 0 and then proving P 0.
n, m: natle n m <-> (exists k : nat, k + n = m)n, m: natle n m <-> (exists k : nat, k + n = m)n, m: natle n m -> exists k : nat, k + n = mn, m: nat(exists k : nat, k + n = m) -> le n mn, m: natle n m -> exists k : nat, k + n = mn, m: nat
h: le n mexists k : nat, k + n = mn: natexists k : nat, k + n = nn, m: nat
h: le n m
ih: exists k : nat, k + n = mexists k : nat, k + n = S mn: natexists k : nat, k + n = nreflexivity.n: nat0 + n = nn, m: nat
h: le n m
ih: exists k : nat, k + n = mexists k : nat, k + n = S mn, m: nat
h: le n m
k: nat
ih: k + n = mexists k0 : nat, k0 + n = S mn, k: nat
h: le n (k + n)exists k0 : nat, k0 + n = S (k + n)n, k: nat
h: le n (k + n)S k + n = S (k + n)reflexivity.n, k: nat
h: le n (k + n)S (k + n) = S (k + n)n, m: nat(exists k : nat, k + n = m) -> le n m
Instead of doing intro x and then destruct x as [a b] we can do intros [a b].
n, m, k: nat
h: k + n = mle n mn, k: natle n (k + n)n: natle n (0 + n)n, k: nat
ih: le n (k + n)le n (S k + n)n: natle n (0 + n)constructor.n: natle n nn, k: nat
ih: le n (k + n)le n (S k + n)n, k: nat
ih: le n (k + n)le n (S (k + n))assumption. Qed.n, k: nat
ih: le n (k + n)le n (k + n)
We can also show transitivity.
n, m, k: natle n m -> le m k -> le n kn, m, k: natle n m -> le m k -> le n kn, m, k: nat
hnm: le n m
hmk: le m kle n kn, m: nat
hnm: le n mle n mn, m: nat
hnm: le n m
k: nat
hmk: le m k
ih: le n m -> le n kle n (S k)assumption.n, m: nat
hnm: le n mle n mn, m: nat
hnm: le n m
k: nat
hmk: le m k
ih: le n m -> le n kle n (S k)n, m: nat
hnm: le n m
k: nat
hmk: le m k
ih: le n m -> le n kle n kassumption. Qed.n, m: nat
hnm: le n m
k: nat
hmk: le m k
ih: le n m -> le n kle n m
Exercise: Another definition of le
Inductive le' : nat -> nat -> Prop :=
| leO' n : le' 0 n
| leS' n m : le' n m -> le' (S n) (S m).Spell out the induction principle of le' without checking it.
Notice that it's not the same definition as le above.
⚠️ Do not forget this one.
Prove the following and lemmas that you need by induction.
Do not use the standard library or lia!
n, m: natle' n m <-> le n mAdmitted.n, m: natle' n m <-> le n m
Exercise: Boolean comparison
n, m: natleb n m = true <-> le n mAdmitted.n, m: natleb n m = true <-> le n m
Nested inductive types
Finally, you can define more inductive types by using what is called nesting. In the type below, you define a tree as something that contains a list of trees.
From Stdlib Require Import Lia List. Import ListNotations.Inductive Forall {A} (P : A -> Prop) : list A -> Prop := | Forall_nil : Forall P nil | Forall_cons a l : P a -> Forall P l -> Forall P (a :: l).rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P rforall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P rrtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P rforall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P rrtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
r: rtree AP rrtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
ts: list (rtree A)P (N A a ts)rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
ts: list (rtree A)Forall P tsrtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: AForall P []rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsForall P (a0 :: ts)econstructor.rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: AForall P []rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsForall P (a0 :: ts)rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsP a0rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsForall P tsrtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsP a0assumption.rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsforall (a1 : A) (ts0 : list (rtree A)), Forall P ts0 -> P (N A a1 ts0)assumption. Qed. Fixpoint size_rtree {A} (t : rtree A) {struct t} : nat := let size_list := fix size_list {A} (l : list (rtree A)) {struct l} : nat := match l with | nil => 0 | cons t tl => size_rtree t + size_list tl end in match t with | N _ a l => 1 + size_list l end. Fixpoint nodes_rtree {A} (t : rtree A) {struct t} : list A := let nodes_list := fix nodes_list {A} (l : list (rtree A)) {struct l} : list A := match l with | nil => nil | cons t tl => nodes_rtree t ++ nodes_list tl end in match t with | N _ a l => a :: nodes_list l end.rtree_ind': forall (A : Type) (P : rtree A -> Prop), (forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)) -> forall r : rtree A, P r
A: Type
P: rtree A -> Prop
IH: forall (a : A) (ts : list (rtree A)), Forall P ts -> P (N A a ts)
a: A
a0: rtree A
ts: list (rtree A)
IHts: Forall P tsForall P tsA: Type
t: rtree Alength (nodes_rtree t) = size_rtree tA: Type
t: rtree Alength (nodes_rtree t) = size_rtree tA: Typeforall t : rtree A, length (nodes_rtree t) = size_rtree tA: Typeforall (a : A) (ts : list (rtree A)), Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) ts -> length (nodes_rtree (N A a ts)) = size_rtree (N A a ts)A: Type
a: A
ts: list (rtree A)
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) tslength (nodes_rtree (N A a ts)) = size_rtree (N A a ts)A: Type
a: Alength (nodes_rtree (N A a [])) = size_rtree (N A a [])A: Type
a: A
a0: rtree A
l: list (rtree A)
H: length (nodes_rtree a0) = size_rtree a0
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) l
IHIH: length (nodes_rtree (N A a l)) = size_rtree (N A a l)length (nodes_rtree (N A a (a0 :: l))) = size_rtree (N A a (a0 :: l))A: Type
a: Alength (nodes_rtree (N A a [])) = size_rtree (N A a [])reflexivity.A: Type
a: A1 = 1A: Type
a: A
a0: rtree A
l: list (rtree A)
H: length (nodes_rtree a0) = size_rtree a0
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) l
IHIH: length (nodes_rtree (N A a l)) = size_rtree (N A a l)length (nodes_rtree (N A a (a0 :: l))) = size_rtree (N A a (a0 :: l))A: Type
a: A
a0: rtree A
l: list (rtree A)
H: length (nodes_rtree a0) = size_rtree a0
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) l
IHIH: length (nodes_rtree (N A a l)) = size_rtree (N A a l)S (length (nodes_rtree a0 ++ (fix nodes_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree t ++ nodes_list A0 tl end) A l)) = S (size_rtree a0 + (fix size_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree t + size_list A0 tl end) A l)A: Type
a: A
a0: rtree A
l: list (rtree A)
H: length (nodes_rtree a0) = size_rtree a0
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) l
IHIH: length (nodes_rtree (N A a l)) = size_rtree (N A a l)S (length (nodes_rtree a0) + length ((fix nodes_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree t ++ nodes_list A0 tl end) A l)) = S (size_rtree a0 + (fix size_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree t + size_list A0 tl end) A l)A: Type
a: A
a0: rtree A
l: list (rtree A)
H: length (nodes_rtree a0) = size_rtree a0
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) l
IHIH: length (nodes_rtree (N A a l)) = size_rtree (N A a l)S (size_rtree a0 + length ((fix nodes_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree t ++ nodes_list A0 tl end) A l)) = S (size_rtree a0 + (fix size_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree t + size_list A0 tl end) A l)lia. Qed. Scheme All for list. Inductive rtree' (A : Type) := | N' (a : A) (ts : list (rtree' A)).A: Type
a: A
a0: rtree A
l: list (rtree A)
H: length (nodes_rtree a0) = size_rtree a0
IH: Forall (fun r : rtree A => length (nodes_rtree r) = size_rtree r) l
IHIH: S (length ((fix nodes_list (A : Type) (l : list (rtree A)) {struct l} : list A := match l with | [] => [] | t :: tl => nodes_rtree t ++ nodes_list A tl end) A l)) = S ((fix size_list (A : Type) (l : list (rtree A)) {struct l} : nat := match l with | [] => 0 | t :: tl => size_rtree t + size_list A tl end) A l)S (size_rtree a0 + length ((fix nodes_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree t ++ nodes_list A0 tl end) A l)) = S (size_rtree a0 + (fix size_list (A0 : Type) (l0 : list (rtree A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree t + size_list A0 tl end) A l)Fixpoint size_rtree' {A} (t : rtree' A) {struct t} : nat := let size_list := fix size_list {A} (l : list (rtree' A)) {struct l} : nat := match l with | nil => 0 | cons t tl => size_rtree' t + size_list tl end in match t with | N' _ a l => 1 + size_list l end. Fixpoint nodes_rtree' {A} (t : rtree' A) {struct t} : list A := let nodes_list := fix nodes_list {A} (l : list (rtree' A)) {struct l} : list A := match l with | nil => nil | cons t tl => nodes_rtree' t ++ nodes_list tl end in match t with | N' _ a l => a :: nodes_list l end.A: Type
t: rtree' Alength (nodes_rtree' t) = size_rtree' tA: Type
t: rtree' Alength (nodes_rtree' t) = size_rtree' tA: Type
a: A
ts: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) tslength (nodes_rtree' (N' A a ts)) = size_rtree' (N' A a ts)A: Type
a: Alength (nodes_rtree' (N' A a [])) = size_rtree' (N' A a [])A: Type
a: A
a0: rtree' A
p: length (nodes_rtree' a0) = size_rtree' a0
l: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) l
IHX: length (nodes_rtree' (N' A a l)) = size_rtree' (N' A a l)length (nodes_rtree' (N' A a (a0 :: l))) = size_rtree' (N' A a (a0 :: l))A: Type
a: Alength (nodes_rtree' (N' A a [])) = size_rtree' (N' A a [])reflexivity.A: Type
a: A1 = 1A: Type
a: A
a0: rtree' A
p: length (nodes_rtree' a0) = size_rtree' a0
l: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) l
IHX: length (nodes_rtree' (N' A a l)) = size_rtree' (N' A a l)length (nodes_rtree' (N' A a (a0 :: l))) = size_rtree' (N' A a (a0 :: l))A: Type
a: A
a0: rtree' A
p: length (nodes_rtree' a0) = size_rtree' a0
l: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) l
IHX: length (nodes_rtree' (N' A a l)) = size_rtree' (N' A a l)S (length (nodes_rtree' a0 ++ (fix nodes_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree' t ++ nodes_list A0 tl end) A l)) = S (size_rtree' a0 + (fix size_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree' t + size_list A0 tl end) A l)A: Type
a: A
a0: rtree' A
p: length (nodes_rtree' a0) = size_rtree' a0
l: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) l
IHX: length (nodes_rtree' (N' A a l)) = size_rtree' (N' A a l)S (length (nodes_rtree' a0) + length ((fix nodes_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree' t ++ nodes_list A0 tl end) A l)) = S (size_rtree' a0 + (fix size_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree' t + size_list A0 tl end) A l)A: Type
a: A
a0: rtree' A
p: length (nodes_rtree' a0) = size_rtree' a0
l: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) l
IHX: length (nodes_rtree' (N' A a l)) = size_rtree' (N' A a l)S (size_rtree' a0 + length ((fix nodes_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree' t ++ nodes_list A0 tl end) A l)) = S (size_rtree' a0 + (fix size_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree' t + size_list A0 tl end) A l)lia. Qed.A: Type
a: A
a0: rtree' A
p: length (nodes_rtree' a0) = size_rtree' a0
l: list (rtree' A)
X: list_all (rtree' A) (fun t : rtree' A => length (nodes_rtree' t) = size_rtree' t) l
IHX: S (length ((fix nodes_list (A : Type) (l : list (rtree' A)) {struct l} : list A := match l with | [] => [] | t :: tl => nodes_rtree' t ++ nodes_list A tl end) A l)) = S ((fix size_list (A : Type) (l : list (rtree' A)) {struct l} : nat := match l with | [] => 0 | t :: tl => size_rtree' t + size_list A tl end) A l)S (size_rtree' a0 + length ((fix nodes_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : list A0 := match l0 with | [] => [] | t :: tl => nodes_rtree' t ++ nodes_list A0 tl end) A l)) = S (size_rtree' a0 + (fix size_list (A0 : Type) (l0 : list (rtree' A0)) {struct l0} : nat := match l0 with | [] => 0 | t :: tl => size_rtree' t + size_list A0 tl end) A l)
Exercise: Unlabelled trees
Consider the following type of trees, without labels.
Can you give an element of type tree?
Module Unlabelled. Inductive tree := | N (ts : list tree). Definition ex4 : tree := REPLACE_ME. End Unlabelled.
Exercise: All and Forall
Here is a variant of Forall in Type. We also make the arguments of
the constructors of All and Forall implicit.
Inductive All {X} (P : X -> Type) : list X -> Type := | All_nil : All P nil | All_cons x l : P x -> All P l -> All P (x :: l). Arguments All_nil {X P}. Arguments All_cons {X P}. Arguments Forall_nil {A P}. Arguments Forall_cons {A P}.
Give the induction principles for both All and Forall. For every argument,
state whether it is a parameter, non-uniform parameter, or index.
Prove the following.
A: Type
P: A -> Prop
l: list AAll P l -> Forall P lAdmitted.A: Type
P: A -> Prop
l: list AAll P l -> Forall P lA: Type
P: A -> Prop
l1, l2: list AForall P l1 -> Forall P l2 -> Forall P (l1 ++ l2)Admitted.A: Type
P: A -> Prop
l1, l2: list AForall P l1 -> Forall P l2 -> Forall P (l1 ++ l2)
Exercise: Labelled trees
Extend the type
Unlabelled.treeto have labelled nodes.Prove a bijection between the new type and the mutual
treefrom the first part, ie. define functions from one to the other and show that they invert each other.
Non-uniform parameters
We can also define the notion of reflexive transitive closure of a relation R : A -> A -> Prop on A like so.
Inductive rtclos {A} (R : A -> A -> Prop) (x : A) : A -> Prop :=
| refl : rtclos R x x
| incl y : R x y -> rtclos R x y
| trans y z : rtclos R x y -> rtclos R y z -> rtclos R x z.In @rtclos A R x y we have
Ais a parameter. It is marked as implicit, because it can be inferred from the type ofR.Ris a parameter.xis a non-uniform parameter. That means it looks like a parameter in the conclusion of constructors, and it does not need to be repeatedly introduced for every constructor.yis an index.
Arguments trans {A R}.Then le is just the closure of being the successor.
n, m: natle n m <-> rtclos (fun n0 m0 : nat => m0 = S n0) n mn, m: natle n m <-> rtclos (fun n0 m0 : nat => m0 = S n0) n mn, m: natle n m -> rtclos (fun n0 m0 : nat => m0 = S n0) n mn, m: natrtclos (fun n0 m0 : nat => m0 = S n0) n m -> le n mn, m: natle n m -> rtclos (fun n0 m0 : nat => m0 = S n0) n mn, m: nat
h: le n mrtclos (fun n0 m0 : nat => m0 = S n0) n mn: natrtclos (fun n0 m : nat => m = S n0) n nn, m: nat
h: le n m
ih: rtclos (fun n m : nat => m = S n) n mrtclos (fun n0 m0 : nat => m0 = S n0) n (S m)constructor.n: natrtclos (fun n0 m : nat => m = S n0) n nn, m: nat
h: le n m
ih: rtclos (fun n m : nat => m = S n) n mrtclos (fun n0 m0 : nat => m0 = S n0) n (S m)
If we just apply constructor then it's going to pick incl which is not what we want. What we want is to use trans:
n, m: nat
h: le n m
ih: rtclos (fun n m : nat => m = S n) n mrtclos (fun n0 m0 : nat => m0 = S n0) n mn, m: nat
h: le n m
ih: rtclos (fun n m : nat => m = S n) n mrtclos (fun n0 m0 : nat => m0 = S n0) m (S m)n, m: nat
h: le n m
ih: rtclos (fun n m : nat => m = S n) n mrtclos (fun n0 m0 : nat => m0 = S n0) m (S m)
We just applied assumption to the first goal this way.
It can be nicer than having many levels of nested bullets.
reflexivity.n, m: nat
h: le n m
ih: rtclos (fun n m : nat => m = S n) n mS m = S mn, m: natrtclos (fun n0 m0 : nat => m0 = S n0) n m -> le n mn, m: nat
h: rtclos (fun n m : nat => m = S n) n mle n mn: natle n nn, m: nat
h: m = S nle n mn, m, k: nat
hnm: rtclos (fun n m : nat => m = S n) n m
hmk: rtclos (fun n m : nat => m = S n) m k
ihnm: le n m
ihmk: le m kle n kconstructor.n: natle n nn, m: nat
h: m = S nle n mn: natle n (S n)constructor.n: natle n nn, m, k: nat
hnm: rtclos (fun n m : nat => m = S n) n m
hmk: rtclos (fun n m : nat => m = S n) m k
ihnm: le n m
ihmk: le m kle n kall: assumption. Qed.n, m, k: nat
hnm: rtclos (fun n m : nat => m = S n) n m
hmk: rtclos (fun n m : nat => m = S n) m k
ihnm: le n m
ihmk: le m kle n mn, m, k: nat
hnm: rtclos (fun n m : nat => m = S n) n m
hmk: rtclos (fun n m : nat => m = S n) m k
ihnm: le n m
ihmk: le m kle m k
Exercise: Membership in a list
Fixpoint In {A} (x : A) (l : list A) : Prop :=
match l with
| nil => False
| y :: m => y = x \/ In x m
end.Propose an inductive definition of membership (add the missing constructor(s)).
Inductive In_i {A} : A -> list A -> Prop := .A: Type
x: A
l: list AIn x l <-> In_i x lAdmitted.A: Type
x: A
l: list AIn x l <-> In_i x l
Exercise: Reflexive transitive closure
Here is another definition of the reflexive transitive closure.
Inductive rtclos' {A} (R : A -> A -> Prop) : A -> A -> Prop :=
| refl' x : rtclos' R x x
| trans' x y z : R x y -> rtclos' R y z -> rtclos' R x z.Hint: You can use apply f with u to provide u as argument to f when
Rocq can't guess it on its own.
forall (A : Type) (R : A -> A -> Prop) (x y z : A), rtclos' R x y -> rtclos' R y z -> rtclos' R x zAdmitted.forall (A : Type) (R : A -> A -> Prop) (x y z : A), rtclos' R x y -> rtclos' R y z -> rtclos' R x zforall (A : Type) (R : A -> A -> Prop) (x y : A), rtclos R x y <-> rtclos' R x yAdmitted.forall (A : Type) (R : A -> A -> Prop) (x y : A), rtclos R x y <-> rtclos' R x y
Exercise: Factorial and Fibonacci (advanced)
This and the following exercises are more advanced. Make sure you manage to do the previous exercises before. Do not hesitate to ask for help.
Define the factorial function in two ways:
once using
Fixpointand once using
iter
Prove that both yield the same value for all arguments.
⚠️ Do not forget this one. We have not stated the lemmas this time.
Define the Fibonacci function, using only iter (no recursive definition).
As a reminder fib 0 = 0, fib 1 = 1 and fib (n+2) = fib (n+1) + fib n.
Then prove that it satisfies these equations.
Module Fib_Iter. Definition fib (n : nat) : nat := REPLACE_ME.fib 0 = 0Admitted.fib 0 = 0fib 1 = 1Admitted.fib 1 = 1forall n : nat, fib (S (S n)) = fib n + fib (S n)Admitted. End Fib_Iter.forall n : nat, fib (S (S n)) = fib n + fib (S n)
Exercise: Cantor pairing (advanced)
Here is an implementation of the Cantor pairing and its inverse function,
a bijection between nat * nat and nat.
From Stdlib Require Import PeanoNat. Fixpoint to_nat' n := match n with | 0 => 0 | S i => S i + to_nat' i end.
Note the following notation '(x, y) is performing pattern matching
implicitly.
Definition to_nat '(x, y) : nat := y + to_nat' (y + x). Fixpoint of_nat (n : nat) : nat * nat := match n with | 0 => (0,0) | S i => let '(x,y) := of_nat i in match x with | 0 => (S y, 0) | S x => (x, S y) end end.
Show that of_nat is a left inverse for to_nat.
Hint: First show forall n p, to_nat p = n -> of_nat n = p.
forall p : nat * nat, of_nat (to_nat p) = pAdmitted.forall p : nat * nat, of_nat (to_nat p) = p
Show to_nat is injective.
forall p q : nat * nat, to_nat p = to_nat q -> p = qAdmitted.forall p q : nat * nat, to_nat p = to_nat q -> p = q
Show to_nat is a left inverse for of_nat.
forall n : nat, to_nat (of_nat n) = nAdmitted.forall n : nat, to_nat (of_nat n) = n
Show of_nat is injective.
forall n m : nat, of_nat n = of_nat m -> n = mAdmitted.forall n m : nat, of_nat n = of_nat m -> n = m
Exercise: Church encodings (advanced)
It is possible to encode natural numbers (and other data types) using
so-called Church encodings that you might have seen in a λ-calculus class.
Below, num is the definition of Church numerals.
Definition num := forall (X : Prop) (s : X -> X) (z : X), X. Definition zero : num := fun X s z => z. Definition succ : num -> num := fun n X s z => s (n X s z). Fixpoint from_nat n : num := match n with | 0 => zero | S n => succ (from_nat n) end. Definition add : num -> num -> num := fun n m X s z => n X s (m X s z).forall n m : nat, add (from_nat n) (from_nat m) = from_nat (n + m)Admitted. Definition mul : num -> num -> num := REPLACE_ME.forall n m : nat, add (from_nat n) (from_nat m) = from_nat (n + m)forall n m : nat, mul (from_nat n) (from_nat m) = from_nat (n * m)Admitted.forall n m : nat, mul (from_nat n) (from_nat m) = from_nat (n * m)
Define the power function (check out the lemma to make sure you see what it should give).
Definition exp : num -> num -> num := REPLACE_ME.n, m: natexp (from_nat n) (from_nat m) = from_nat (n ^ m)Admitted.n, m: natexp (from_nat n) (from_nat m) = from_nat (n ^ m)
Define a predecessor function.
Definition pred : num -> num :=
REPLACE_ME.n: natpred (from_nat n) = from_nat (Nat.pred n)Admitted.n: natpred (from_nat n) = from_nat (Nat.pred n)