Proving even + even = even with mutual induction using tactics
Question
I was trying out mutual induction in Coq, the first type I defined was
Inductive IsEven : nat -> Prop :=
| EvenO : IsEven O
| EvenS n : IsOdd n -> IsEven (S n)
with IsOdd : nat -> Prop :=
| OddS n : IsEven n -> IsOdd (S n).
I now wanted to prove that the sum of even numbers is even. I was able to do this using a Fixpoint and pattern matching:
Fixpoint even_plus_even (n m : nat) (evenn : IsEven n) (evenm : IsEven m) :
IsEven (n + m) :=
match evenn with
| EvenO => evenm
| EvenS n' oddn' => EvenS (n' + m) (odd_plus_even n' m oddn' evenm)
end
with odd_plus_even (n m : nat) (oddn : IsOdd n) (evenm : IsEven m) :
IsOdd (n + m) :=
match oddn with
| OddS n' evenn' => OddS (n' + m) (even_plus_even n' m evenn' evenm)
end.
This defines both even_plus_even and odd_plus_even. I would now like to prove this using tactics in a more terse way (preferably without using many predefined lemmas to keep the code as self-contained as possible) but I haven't gotten very far.
Specifically, is it possible to prove both even_plus_even and odd_plus_even using only one Lemma like we can with the Fixpoint?
Edit: Thank you very much for your answers, the Lemma ... with ... syntax was exactly what I was looking for. In fact
even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
evenn: IsEven n
evenm: IsEven mIsEven (n + m)even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
oddn: IsOdd n
evenm: IsEven mIsOdd (n + m)even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
evenn: IsEven n
evenm: IsEven mIsEven (n + m)even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
oddn: IsOdd n
evenm: IsEven mIsOdd (n + m)even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m: nat
evenm: IsEven mIsEven meven_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m, n: nat
H: IsOdd n
evenm: IsEven mIsEven (S (n + m))even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
oddn: IsOdd n
evenm: IsEven mIsOdd (n + m)assumption.even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m: nat
evenm: IsEven mIsEven meven_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m, n: nat
H: IsOdd n
evenm: IsEven mIsEven (S (n + m))auto.even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m, n: nat
H: IsOdd n
evenm: IsEven mIsOdd (n + m)even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
oddn: IsOdd n
evenm: IsEven mIsOdd (n + m)even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m, n: nat
H: IsEven n
evenm: IsEven mIsOdd (S (n + m))auto. Defined.even_plus_even2: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even2: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m, n: nat
H: IsEven n
evenm: IsEven mIsEven (n + m)
generates exactly the same proof term as the Fixpoint in my originial question.
Answer (Vinz)
There is support for mutual induction in Coq. I know of two ways, but I only recall how to use one:
Combined Schemes
Here is how it works:
Scheme IsEven_ind2 := Induction for IsEven Sort Prop with IsOdd_ind2 := Induction for IsOdd Sort Prop. Combined Scheme IsEvenOdd_ind from IsEven_ind2, IsOdd_ind2.
(forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)) /\ (forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m))(forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)) /\ (forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m))forall m : nat, IsEven m -> IsEven (0 + m)forall n : nat, IsOdd n -> (forall m : nat, IsEven m -> IsOdd (n + m)) -> forall m : nat, IsEven m -> IsEven (S n + m)forall n : nat, IsEven n -> (forall m : nat, IsEven m -> IsEven (n + m)) -> forall m : nat, IsEven m -> IsOdd (S n + m)now intros m hm.forall m : nat, IsEven m -> IsEven (0 + m)forall n : nat, IsOdd n -> (forall m : nat, IsEven m -> IsOdd (n + m)) -> forall m : nat, IsEven m -> IsEven (S n + m)h: nat
hn: IsOdd h
hi: forall m : nat, IsEven m -> IsOdd (h + m)
m: nat
hm: IsEven mIsEven (S h + m)h: nat
hn: IsOdd h
hi: forall m : nat, IsEven m -> IsOdd (h + m)
m: nat
hm: IsEven mIsEven (S (h + m))now apply hi.h: nat
hn: IsOdd h
hi: forall m : nat, IsEven m -> IsOdd (h + m)
m: nat
hm: IsEven mIsOdd (h + m)forall n : nat, IsEven n -> (forall m : nat, IsEven m -> IsEven (n + m)) -> forall m : nat, IsEven m -> IsOdd (S n + m)h: nat
hn: IsEven h
hi: forall m : nat, IsEven m -> IsEven (h + m)
m: nat
hm: IsEven mIsOdd (S h + m)h: nat
hn: IsEven h
hi: forall m : nat, IsEven m -> IsEven (h + m)
m: nat
hm: IsEven mIsOdd (S (h + m))now apply hi. Qed.h: nat
hn: IsEven h
hi: forall m : nat, IsEven m -> IsEven (h + m)
m: nat
hm: IsEven mIsEven (h + m)
Lemma with
On this one I just don't know how to finish it but that's a syntax issue iirc:
foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
hn: IsEven nIsEven m -> IsEven (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n: nat
hn: IsEven nforall m : nat, IsEven m -> IsEven (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m: nat
hm: IsEven mIsEven mfoo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
m: nat
hm: IsEven mIsEven (S (p + m))exact hm.foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m: nat
hm: IsEven mIsEven mnow apply EvenS; apply bar.foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
m: nat
hm: IsEven mIsEven (S (p + m))foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n, m: nat
hn: IsOdd n
hm: IsEven mIsOdd (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
m: nat
hm: IsEven mforall n : nat, IsOdd n -> IsOdd (n + m)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n: nat
hn: IsOdd nIsOdd (n + 0)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
n: nat
hn: IsOdd nIsOdd (n + S p)now apply bar; [exact hn | apply EvenO].foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
n: nat
hn: IsOdd nIsOdd (n + 0)foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
n: nat
hn: IsOdd nIsOdd (n + S p)now apply EvenS.foo: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
bar: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
n: nat
hn: IsOdd nIsEven (S p)
Edit: Here is a valid syntax for the Lemma with solution.
foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
n: nat
evenn: IsEven nforall m : nat, IsEven m -> IsEven (n + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
n: nat
oddn: IsOdd nforall m : nat, IsEven m -> IsOdd (n + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
n: nat
evenn: IsEven nforall m : nat, IsEven m -> IsEven (n + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
n: nat
oddn: IsOdd nforall m : nat, IsEven m -> IsOdd (n + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
n: nat
evenn: IsEven nforall m : nat, IsEven m -> IsEven (n + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
m: nat
hm: IsEven mIsEven mfoo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
m: nat
hm: IsEven mIsEven (S (p + m))exact hm.foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
m: nat
hm: IsEven mIsEven mnow apply EvenS; apply bar.foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
p: nat
hp: IsOdd p
m: nat
hm: IsEven mIsEven (S (p + m))foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
n: nat
oddn: IsOdd nforall m : nat, IsEven m -> IsOdd (n + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
p: nat
hp: IsEven pforall m : nat, IsEven m -> IsOdd (S p + m)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
p: nat
hp: IsEven p
n: nat
hn: IsEven nIsOdd (S p + n)foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
p: nat
hp: IsEven p
n: nat
hn: IsEven nIsOdd (S (p + n))now apply foo. Qed.foo: forall n : nat, IsEven n -> forall m : nat, IsEven m -> IsEven (n + m)
bar: forall n : nat, IsOdd n -> forall m : nat, IsEven m -> IsOdd (n + m)
p: nat
hp: IsEven p
n: nat
hn: IsEven nIsEven (p + n)
Answer (larsr)
You can for instance prove both lemmas at the same time by "anding" them together into one lemma, and then extract the left or right part of the clause using proj1 and proj2.
forall n m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))forall n m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))m: nat
evenn: IsEven 0
evenm: IsEven mIsEven (0 + m)m: nat
oddn: IsOdd 0
evenm: IsEven mIsOdd (0 + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
evenn: IsEven (S n)
evenm: IsEven mIsEven (S n + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
oddn: IsOdd (S n)
evenm: IsEven mIsOdd (S n + m)auto.m: nat
evenn: IsEven 0
evenm: IsEven mIsEven (0 + m)inversion oddn.m: nat
oddn: IsOdd 0
evenm: IsEven mIsOdd (0 + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
evenn: IsEven (S n)
evenm: IsEven mIsEven (S n + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
evenn: IsEven (S n)
evenm: IsEven m
He: IsEven n -> IsEven m -> IsEven (n + m)
Ho: IsOdd n -> IsEven m -> IsOdd (n + m)IsEven (S n + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
evenn: IsEven (S n)
evenm: IsEven m
He: IsEven n -> IsEven m -> IsEven (n + m)
Ho: IsOdd n -> IsEven m -> IsOdd (n + m)
n0: nat
H0: IsOdd n
H: n0 = nIsEven (S n + m)auto.n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
evenn: IsEven (S n)
evenm: IsEven m
He: IsEven n -> IsEven m -> IsEven (n + m)
Ho: IsOdd n -> IsEven m -> IsOdd (n + m)
n0: nat
H0: IsOdd n
H: n0 = nIsOdd ((fix add (n m : nat) {struct n} : nat := match n with | 0 => m | S p => S (add p m) end) n m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
oddn: IsOdd (S n)
evenm: IsEven mIsOdd (S n + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
oddn: IsOdd (S n)
evenm: IsEven m
He: IsEven n -> IsEven m -> IsEven (n + m)
Ho: IsOdd n -> IsEven m -> IsOdd (n + m)IsOdd (S n + m)n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
oddn: IsOdd (S n)
evenm: IsEven m
He: IsEven n -> IsEven m -> IsEven (n + m)
Ho: IsOdd n -> IsEven m -> IsOdd (n + m)
n0: nat
H0: IsEven n
H: n0 = nIsOdd (S n + m)auto. Qed. Definition even_plus_even n m := proj1 (even_or_odd_plus_even n m). Definition odd_plus_even n m := proj2 (even_or_odd_plus_even n m).n: nat
IHn: forall m : nat, (IsEven n -> IsEven m -> IsEven (n + m)) /\ (IsOdd n -> IsEven m -> IsOdd (n + m))
m: nat
oddn: IsOdd (S n)
evenm: IsEven m
He: IsEven n -> IsEven m -> IsEven (n + m)
Ho: IsOdd n -> IsEven m -> IsOdd (n + m)
n0: nat
H0: IsEven n
H: n0 = nIsEven ((fix add (n m : nat) {struct n} : nat := match n with | 0 => m | S p => S (add p m) end) n m)
gives you
Notice that the two clauses share share the n and m, which you need if the clauses can't be proved separately because they need to depend on each other. (In this particular case they don't, though. You could prove the statements separately, as Anton showed.)
EDIT: just saw Vinz solution. I didn't know Lemma had the with syntax (thanks for showing!), but that makes sense and is I think a neater way to do this mutually dependent definition.
even_plus_even: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)even_plus_even: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)even_plus_even: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)even_plus_even: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)induction 1; simpl; auto using EvenS.even_plus_even: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)induction 1; simpl; auto using OddS. Qed.even_plus_even: forall n m : nat, IsEven n -> IsEven m -> IsEven (n + m)
odd_plus_even: forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)forall n m : nat, IsOdd n -> IsEven m -> IsOdd (n + m)
A: Actually, both Lemma and Fixpoint are just aliases for Theorem, see here, which means that you can also define Fixpoint definitions with proof tactics, without giving the explicit proof term upfront. By the way, the refine tactic is useful if you want to give parts of the proof term explicitly, while leaving holes to fill in by tactics.