I wrote a proof for this question, but it's long and kind of terrible, see Appendix A.
There is one specific thing that I want to focus on and that is the induction step in make_double_induction
.
Program Fixpoint make_double_induction
P
(H0 : P 0 0)
(Hn : forall n, P 0 n -> P 0 (S n))
(Hm : forall m, P m 0 -> P (S m) 0)
(Hmn : forall m n, P m n -> P (S m) (S n))
(m : nat)
(n : nat) : P m n :=
match (m, n) with
| (O, _) => make_double_induction_lemma P H0 Hn Hm Hmn n
| (S m, O) => Hm m (make_double_induction _ _ _ _ _ _ _)
| (S m, S n) => Hmn m n (make_double_induction _ _ _ _ _ _ _)
end.
In order to convince Rocq that this function terminates, I broke out the case that counts down on the second argument and put it in a lemma that was also structurally inductive. (The lemma takes a bunch of unnecessary arguments but that is neither here nor there).
Anyway, there are various ways of proving to another human that the version of make_double_induction
with the lemma unfolded terminates, such as nothing that it always counts down on m + n
.
I am curious if there's a way of writing this function in a way that will be accepted by the Coq termination checker without hiding make_double_induction_lemma
Appendix A
Lemma leibniz : forall {T} {U} (a : T) b (f : T -> U), a = b -> f a = f b.
Proof.
intros T U a b f H.
now rewrite H.
Defined.
Lemma pair_extensionality : forall {T} {U} (a : T) (b : U) c d, (a, b) = (c, d) <-> (a = c /\ b = d).
Proof.
intros T U a b c d.
intuition.
- pose proof @leibniz _ _ (a, b) (c, d) fst as I.
now specialize (I H).
- pose proof @leibniz _ _ (a, b) (c, d) snd as I.
now specialize (I H).
- rewrite H0.
now rewrite H1.
Defined.
Lemma debiconditional : forall {A} {B} (x : A <-> B), (A -> B).
Proof. intuition. Defined.
Program Fixpoint make_double_induction_lemma
P
(H0 : P 0 0)
(Hn : forall n, P 0 n -> P 0 (S n))
(Hm : forall m, P m 0 -> P (S m) 0)
(Hmn : forall m n, P m n -> P (S m) (S n))
(n : nat) : P 0 n :=
match n with
| O => _
| S n => Hn n (make_double_induction_lemma _ _ _ _ _ n)
end.
Next Obligation. intros. refine H0. Defined.
Next Obligation. intros. refine H0. Defined.
Next Obligation. intros. refine (Hn n1 X). Defined.
Next Obligation. intros. refine (Hm m X). Defined.
Next Obligation. intros. refine (Hmn m n1 X). Defined.
Program Fixpoint make_double_induction
P
(H0 : P 0 0)
(Hn : forall n, P 0 n -> P 0 (S n))
(Hm : forall m, P m 0 -> P (S m) 0)
(Hmn : forall m n, P m n -> P (S m) (S n))
(m : nat)
(n : nat) : P m n :=
match (m, n) with
| (O, _) => make_double_induction_lemma P H0 Hn Hm Hmn n
| (S m, O) => Hm m (make_double_induction _ _ _ _ _ _ _)
| (S m, S n) => Hmn m n (make_double_induction _ _ _ _ _ _ _)
end.
Next Obligation.
intros.
unfold filtered_var in *.
pose proof (debiconditional (pair_extensionality 0 wildcard' m n)) as H.
specialize (H Heq_anonymous).
now destruct H.
Defined.
Next Obligation. intros. easy. Defined.
Next Obligation. intros. refine (Hn n0 X). Defined.
Next Obligation. intros. refine (Hm m1 X). Defined.
Next Obligation. intros. refine (Hmn m1 n0 X). Defined.
Next Obligation.
intros.
unfold filtered_var in Heq_anonymous.
pose proof (debiconditional (pair_extensionality (S m) 0 m0 n)).
specialize (H Heq_anonymous).
now destruct H.
Defined.
Next Obligation.
intros.
unfold filtered_var in Heq_anonymous.
pose proof (debiconditional (pair_extensionality (S m) 0 m0 n)).
specialize (H Heq_anonymous).
now destruct H.
Defined.
Next Obligation. intros. easy. Defined.
Next Obligation. intros. refine (Hn n1 X). Defined.
Next Obligation. intros. refine (Hm m1 X). Defined.
Next Obligation. intros. refine (Hmn m1 n1 X). Defined.
Next Obligation.
intros.
unfold filtered_var in Heq_anonymous.
pose proof (debiconditional (pair_extensionality (S m) (S n) m0 n0)).
specialize (H Heq_anonymous).
now destruct H.
Defined.
Next Obligation.
intros.
unfold filtered_var in Heq_anonymous.
pose proof (debiconditional (pair_extensionality (S m) (S n) m0 n0)).
specialize (H Heq_anonymous).
now destruct H.
Defined.
```