Wellfounded induction in Coq
Question
Let's say that I know certain natural numbers are good. I know 1 is good, if n is good then 3n is, and if n is good then n+5 is, and those are only ways of constructing good numbers. It seems to me that the adequate formalization of this in Coq is
Inductive good : nat -> Prop :=
| g1 : good 1
| g3 : forall n, good n -> good (n * 3)
| g5 : forall n, good n -> good (n + 5).
However, despite being obvious, the fact that 0 is not good seems not being provable using this definition (because when I invert, in case of g3 I only get the same thing in the hypothesis).
Now it isn't so obvious what exactly are good numbers. And it really seems that I don't need to characterize them totally in order to know that 0 is not good. For example, I can know that 2 is not good just by doing few inversions.
Answer (eponier)
Indeed g3 can be applied an unbounded number of times when trying to disprove good 0. That is why we can think this proof requires induction (and we can see that the auxiliary lemma needed in the solution of @AntonTrunov uses induction). The same idea is used in theorem loop_never_stop of http://www.cis.upenn.edu/~bcpierce/sf/current/Imp.html#lab428.
Require Import Lia.~ good 0~ good 0contra: good 0Falsen: nat
Heqn: n = 0
contra: good nFalseHeqn: 1 = 0Falsen: nat
Heqn: n * 3 = 0
contra: good n
IHcontra: n = 0 -> FalseFalsen: nat
Heqn: n + 5 = 0
contra: good n
IHcontra: n = 0 -> FalseFalsediscriminate.Heqn: 1 = 0Falsen: nat
Heqn: n * 3 = 0
contra: good n
IHcontra: n = 0 -> FalseFalselia.n: nat
Heqn: n * 3 = 0
contra: good n
IHcontra: n = 0 -> Falsen = 0lia. Qed.n: nat
Heqn: n + 5 = 0
contra: good n
IHcontra: n = 0 -> FalseFalse
A: And remember 0 as n is a crucial component of this solution. It sort of replaces the original statement with forall n, n = 0 -> ~ good n.. Great answer!
Answer (Anton Trunov)
This problem needs induction. And induction needs some predicate P : nat -> Prop to work with. A primitive (constant) predicate like (fun n => ~good 0) doesn't give you much: you won't be able to prove the base case for 1 (which corresponds to the constructor g1), because the predicate "forgets" its argument.
So you need to prove some logically equivalent (or stronger) statement which readily will give you the necessary predicate. An example of such equivalent statement is forall n, good n -> n > 0, which you can later use to disprove good 0. The corresponding predicate P is (fun n => n > 0).
Require Import Coq.Arith.Arith. Require Import Coq.micromega.Lia. Inductive good : nat -> Prop := | g1 : good 1 | g3 : forall n, good n -> good (n * 3) | g5 : forall n, good n -> good (n + 5).forall n : nat, good n -> n > 0forall n : nat, good n -> n > 0induction H; lia. Qed.n: nat
H: good nn > 0~ good 0now apply good_gt_O in H. Qed.H: good 0False
Here is a proof of the aforementioned equivalence:
(forall n : nat, good n -> n > 0) <-> ~ good 0(forall n : nat, good n -> n > 0) <-> ~ good 0H: forall n : nat, good n -> n > 0
H0: good 0FalseH: ~ good 0
n: natgood n -> n > 0H: forall n : nat, good n -> n > 0
H0: good 0Falselia.H: forall n : nat, good n -> n > 0
H0: 0 > 0Falsedestruct n; [tauto | lia]. Qed.H: ~ good 0
n: natgood n -> n > 0
And it's easy to show that forall n, n = 0 -> ~ good n which implicitly appears in @eponier's answer is equivalent to ~ good 0 too.
(forall n : nat, n = 0 -> ~ good n) <-> ~ good 0split; intros; subst; auto. Qed.(forall n : nat, n = 0 -> ~ good n) <-> ~ good 0
Now, the corresponding predicate used to prove forall n, n = 0 -> ~ good n is fun n => n = 0 -> False. This can be shown by using manual application of the goal_ind induction principle, automatically generated by Coq:
forall n : nat, n = 0 -> ~ good nforall n : nat, n = 0 -> ~ good nn: nat
Eq: n = 0
contra: good nFalserefine (good_ind (fun n => n = 0 -> False) _ _ _ _ _); try eassumption; intros; lia. Qed.n: nat
Eq: n = 0
contra: good nn = 0 -> False
generalize Eq. introduces n = 0 as a premise to the current goal. Without it the goal to prove would be False and the corresponding predicate would be the boring fun n => False again.