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 hyoptheses. 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 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 implictly 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
The "less than" 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
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)
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 @rclos 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