(* ========================================================================= *)
(* Theory of wellfounded relations. *)
(* *)
(* John Harrison, University of Cambridge Computer Laboratory *)
(* *)
(* (c) Copyright, University of Cambridge 1998 *)
(* (c) Copyright, John Harrison 1998-2007 *)
(* ========================================================================= *)
needs "arith.ml";;
(* ------------------------------------------------------------------------- *)
(* Definition of wellfoundedness for arbitrary (infix) relation << *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("<<",(12,"right"));;
let WF = new_definition
`WF(<<) <=> !P:A->bool. (?x. P(x)) ==> (?x. P(x) /\ !y. y << x ==> ~P(y))`;;
(* ------------------------------------------------------------------------- *)
(* Strengthen it to equality. *)
(* ------------------------------------------------------------------------- *)
let WF_EQ = prove
(`
WF(<<) <=> !P:A->bool. (?x. P(x)) <=> (?x. P(x) /\ !y. y << x ==> ~P(y))`,
REWRITE_TAC[
WF] THEN MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Equivalence of wellfounded induction. *)
(* ------------------------------------------------------------------------- *)
let WF_IND = prove
(`
WF(<<) <=> !P:A->bool. (!x. (!y. y << x ==> P(y)) ==> P(x)) ==> !x. P(x)`,
REWRITE_TAC[
WF] THEN EQ_TAC THEN DISCH_TAC THEN GEN_TAC THEN
POP_ASSUM(MP_TAC o SPEC `\x:A. ~P(x)`) THEN REWRITE_TAC[] THEN MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Equivalence of the "infinite descending chains" version. *)
(* ------------------------------------------------------------------------- *)
let WF_DCHAIN = prove
(`
WF(<<) <=> ~(?s:num->A. !n. s(SUC n) << s(n))`,
REWRITE_TAC[
WF; TAUT `(a <=> ~b) <=> (~a <=> b)`;
NOT_FORALL_THM] THEN
EQ_TAC THEN DISCH_THEN CHOOSE_TAC THENL
[POP_ASSUM(MP_TAC o REWRITE_RULE[NOT_IMP]) THEN
DISCH_THEN(CONJUNCTS_THEN2 (X_CHOOSE_TAC `a:A`) ASSUME_TAC) THEN
SUBGOAL_THEN `!x:A. ?y. P(x) ==> P(y) /\ y << x` MP_TAC THENL
[ASM_MESON_TAC[]; REWRITE_TAC[
SKOLEM_THM]] THEN
DISCH_THEN(X_CHOOSE_THEN `f:A->A` STRIP_ASSUME_TAC) THEN
CHOOSE_TAC(prove_recursive_functions_exist num_RECURSION
`(s(0) = a:A) /\ (!n. s(SUC n) = f(s n))`) THEN
EXISTS_TAC `s:num->A` THEN ASM_REWRITE_TAC[] THEN
SUBGOAL_THEN `!n. P(s n) /\ s(SUC n):A << s(n)`
(fun th -> ASM_MESON_TAC[th]) THEN
INDUCT_TAC THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[];
EXISTS_TAC `\y:A. ?n:num. y = s(n)` THEN REWRITE_TAC[] THEN
ASM_MESON_TAC[]]);;
(* ------------------------------------------------------------------------- *)
(* Equivalent to just *uniqueness* part of recursion. *)
(* ------------------------------------------------------------------------- *)
let WF_UREC = prove
(`
WF(<<) ==>
!H. (!f g x. (!z. z << x ==> (f z = g z)) ==> (H f x = H g x))
==> !(f:A->B) g. (!x. f x = H f x) /\ (!x. g x = H g x)
==> (f = g)`,
REWRITE_TAC[
WF_IND] THEN REPEAT STRIP_TAC THEN REWRITE_TAC[
FUN_EQ_THM] THEN
FIRST_ASSUM MATCH_MP_TAC THEN GEN_TAC THEN
DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN ASM_REWRITE_TAC[]);;
let WF_UREC_WF = prove
(`(!H. (!f g x. (!z. z << x ==> (f z = g z)) ==> (H f x = H g x))
==> !(f:A->bool) g. (!x. f x = H f x) /\ (!x. g x = H g x)
==> (f = g))
==>
WF(<<)`,
REWRITE_TAC[
WF_IND] THEN DISCH_TAC THEN GEN_TAC THEN DISCH_TAC THEN
FIRST_X_ASSUM(MP_TAC o SPEC `\f x. P(x:A) \/ !z:A. z << x ==> f(z)`) THEN
REWRITE_TAC[] THEN
W(C SUBGOAL_THEN (fun t -> REWRITE_TAC[t]) o funpow 2 lhand o snd) THENL
[MESON_TAC[]; DISCH_THEN(MP_TAC o SPECL [`P:A->bool`; `\x:A. T`]) THEN
REWRITE_TAC[
FUN_EQ_THM] THEN ASM_MESON_TAC[]]);;
(* ------------------------------------------------------------------------- *)
(* Stronger form of recursion with "inductive invariant" (Krstic/Matthews). *)
(* ------------------------------------------------------------------------- *)
let WF_REC_INVARIANT = prove
(`
WF(<<)
==> !H S. (!f g x. (!z. z << x ==> (f z = g z) /\ S z (f z))
==> (H f x = H g x) /\ S x (H f x))
==> ?f:A->B. !x. (f x = H f x)`,
let lemma = prove_inductive_relations_exist
`!f:A->B x. (!z. z << x ==> R z (f z)) ==> R x (H f x)` in
REWRITE_TAC[WF_IND] THEN REPEAT STRIP_TAC THEN
X_CHOOSE_THEN `R:A->B->bool` STRIP_ASSUME_TAC lemma THEN
SUBGOAL_THEN `!x:A. ?!y:B. R x y` (fun th -> ASM_MESON_TAC[th]) THEN
FIRST_X_ASSUM MATCH_MP_TAC THEN REPEAT STRIP_TAC THEN
FIRST_X_ASSUM(fun th -> GEN_REWRITE_TAC BINDER_CONV [th]) THEN
SUBGOAL_THEN `!x:A y:B. R x y ==> S x y` MP_TAC THEN ASM_MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Equivalent to just *existence* part of recursion. *)
(* ------------------------------------------------------------------------- *)
let WF_REC = prove
(`
WF(<<)
==> !H. (!f g x. (!z. z << x ==> (f z = g z)) ==> (H f x = H g x))
==> ?f:A->B. !x. f x = H f x`,
REPEAT STRIP_TAC THEN
FIRST_X_ASSUM(MATCH_MP_TAC o MATCH_MP
WF_REC_INVARIANT) THEN
EXISTS_TAC `\x:A y:B. T` THEN ASM_REWRITE_TAC[]);;
let WF_REC_WF = prove
(`(!H. (!f g x. (!z. z << x ==> (f z = g z)) ==> (H f x = H g x))
==> ?f:A->num. !x. f x = H f x)
==>
WF(<<)`,
DISCH_TAC THEN REWRITE_TAC[
WF_DCHAIN] THEN
DISCH_THEN(X_CHOOSE_TAC `x:num->A`) THEN
SUBGOAL_THEN `!n. (x:num->A)(@m. x(m) << x(n)) << x(n)` ASSUME_TAC THENL
[CONV_TAC(BINDER_CONV SELECT_CONV) THEN ASM_MESON_TAC[]; ALL_TAC] THEN
FIRST_ASSUM(MP_TAC o SPEC
`\f:A->num. \y:A. if ?p:num. y = x(p)
then SUC(f(x(@m. x(m) << y)))
else 0`) THEN
REWRITE_TAC[NOT_IMP] THEN CONJ_TAC THENL
[REPEAT STRIP_TAC THEN COND_CASES_TAC THEN REWRITE_TAC[] THEN
FIRST_ASSUM(X_CHOOSE_THEN `p:num` SUBST_ALL_TAC) THEN
AP_TERM_TAC THEN FIRST_ASSUM MATCH_MP_TAC THEN
FIRST_ASSUM MATCH_ACCEPT_TAC; ALL_TAC] THEN
DISCH_THEN(X_CHOOSE_THEN `f:A->num` MP_TAC) THEN
DISCH_THEN(MP_TAC o GEN `n:num` o SPEC `(x:num->A) n`) THEN
SUBGOAL_THEN `!n. ?p. (x:num->A) n = x p` (fun th -> REWRITE_TAC[th]) THENL
[MESON_TAC[]; DISCH_TAC] THEN
SUBGOAL_THEN `!n:num. ?k. f(x(k):A) < f(x(n))` ASSUME_TAC THENL
[GEN_TAC THEN EXISTS_TAC `@m:num. x(m):A << x(n)` THEN
FIRST_ASSUM(fun th -> GEN_REWRITE_TAC RAND_CONV [th]) THEN REWRITE_TAC[
LT];
MP_TAC(SPEC `\n:num. ?i:num. n = f(x(i):A)`
num_WOP) THEN
REWRITE_TAC[] THEN ASM_MESON_TAC[]]);;
(* ------------------------------------------------------------------------- *)
(* Combine the two versions of the recursion theorem. *)
(* ------------------------------------------------------------------------- *)
let WF_EREC = prove
(`
WF(<<) ==>
!H. (!f g x. (!z. z << x ==> (f z = g z)) ==> (H f x = H g x))
==> ?!f:A->B. !x. f x = H f x`,
(* ------------------------------------------------------------------------- *)
(* Some preservation theorems for wellfoundedness. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("<<<",(12,"right"));;
let WF_SUBSET = prove
(`(!(x:A) y. x << y ==> x <<< y) /\
WF(<<<) ==>
WF(<<)`,
DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC) THEN REWRITE_TAC[
WF] THEN
DISCH_TAC THEN GEN_TAC THEN DISCH_THEN(ANTE_RES_THEN MP_TAC) THEN
UNDISCH_TAC `!(x:A) (y:A). x << y ==> x <<< y` THEN MESON_TAC[]);;
let WF_MEASURE_GEN = prove
(`!m:A->B.
WF(<<) ==>
WF(\x x'. m x << m x')`,
GEN_TAC THEN REWRITE_TAC[
WF_IND] THEN REPEAT STRIP_TAC THEN
FIRST_ASSUM(MP_TAC o SPEC `\y:B. !x:A. (m(x) = y) ==> P x`) THEN
UNDISCH_TAC `!x. (!y. (m:A->B) y << m x ==> P y) ==> P x` THEN
REWRITE_TAC[] THEN MESON_TAC[]);;
let WF_LEX_DEPENDENT = prove
(`!R:A->A->bool S:A->B->B->bool.
WF(R) /\ (!a.
WF(S a))
==>
WF(\(r1,s1) (r2,s2). R r1 r2 \/ (r1 = r2) /\ S r1 s1 s2)`,
REPEAT GEN_TAC THEN REWRITE_TAC[
WF] THEN STRIP_TAC THEN
X_GEN_TAC `P:A#B->bool` THEN REWRITE_TAC[
LEFT_IMP_EXISTS_THM] THEN
GEN_REWRITE_TAC I [
FORALL_PAIR_THM] THEN
MAP_EVERY X_GEN_TAC [`a0:A`; `b0:B`] THEN DISCH_TAC THEN
FIRST_X_ASSUM(MP_TAC o SPEC `\a:A. ?b:B. P(a,b)`) THEN
REWRITE_TAC[
LEFT_IMP_EXISTS_THM] THEN
DISCH_THEN(MP_TAC o SPECL [`a0:A`; `b0:B`]) THEN ASM_REWRITE_TAC[] THEN
DISCH_THEN(X_CHOOSE_THEN `a:A` (CONJUNCTS_THEN2 MP_TAC ASSUME_TAC)) THEN
DISCH_THEN(X_CHOOSE_TAC `b1:B`) THEN
FIRST_X_ASSUM(MP_TAC o SPECL [`a:A`; `\b. (P:A#B->bool) (a,b)`]) THEN
REWRITE_TAC[
LEFT_IMP_EXISTS_THM] THEN
DISCH_THEN(MP_TAC o SPEC `b1:B`) THEN ASM_REWRITE_TAC[] THEN
DISCH_THEN(X_CHOOSE_THEN `b:B` (CONJUNCTS_THEN2 MP_TAC ASSUME_TAC)) THEN
DISCH_TAC THEN EXISTS_TAC `(a:A,b:B)` THEN ASM_REWRITE_TAC[] THEN
REWRITE_TAC[
FORALL_PAIR_THM] THEN ASM_MESON_TAC[]);;
let WF_LEX = prove
(`!R:A->A->bool S:B->B->bool.
WF(R) /\
WF(S)
==>
WF(\(r1,s1) (r2,s2). R r1 r2 \/ (r1 = r2) /\ S s1 s2)`,
let WF_POINTWISE = prove
(`
WF((<<) :A->A->bool) /\
WF((<<<) :B->B->bool)
==>
WF(\(x1,y1) (x2,y2). x1 << x2 /\ y1 <<< y2)`,
STRIP_TAC THEN MATCH_MP_TAC(GEN_ALL
WF_SUBSET) THEN EXISTS_TAC
`\(x1,y1) (x2,y2). x1 << x2 \/ (x1:A = x2) /\ (y1:B) <<< (y2:B)` THEN
CONJ_TAC THENL
[REWRITE_TAC[
FORALL_PAIR_THM] THEN CONV_TAC TAUT;
MATCH_MP_TAC
WF_LEX THEN ASM_REWRITE_TAC[]]);;
(* ------------------------------------------------------------------------- *)
(* Wellfoundedness properties of natural numbers. *)
(* ------------------------------------------------------------------------- *)
let WF_REC_num = prove
(`!H. (!f g n. (!m. m < n ==> (f m = g m)) ==> (H f n = H g n))
==> ?f:num->A. !n. f n = H f n`,
(* ------------------------------------------------------------------------- *)
(* Natural number measures (useful in program verification). *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Trivially, a WF relation is irreflexive. *)
(* ------------------------------------------------------------------------- *)
let WF_REFL = prove
(`!x:A.
WF(<<) ==> ~(x << x)`,
GEN_TAC THEN REWRITE_TAC[
WF] THEN
DISCH_THEN(MP_TAC o SPEC `\y:A. y = x`) THEN
REWRITE_TAC[] THEN MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Even more trivially, the everywhere-false relation is wellfounded. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Tail recursion. *)
(* ------------------------------------------------------------------------- *)
let WF_REC_TAIL = prove
(`!P g h. ?f:A->B. !x. f x = if P(x) then f(g x) else h x`,
let lemma1 = prove
(`~(P 0) ==> ((?n. P(SUC n)) <=> (?n. P(n)))`,
MESON_TAC[num_CASES; NOT_SUC])
and lemma2 = prove
(`(P 0) ==> ((!m. m < n ==> P(SUC m)) <=> (!m. m < SUC n ==> P(m)))`,
REPEAT(DISCH_TAC ORELSE EQ_TAC) THEN INDUCT_TAC THEN
ASM_MESON_TAC[LT_SUC; LT_0]) in
REPEAT GEN_TAC THEN
MP_TAC(GEN `x:A` (ISPECL [`x:A`; `\y:A n:num. g(y):A`] num_RECURSION)) THEN
REWRITE_TAC[SKOLEM_THM; FORALL_AND_THM] THEN
DISCH_THEN(X_CHOOSE_THEN `s:A->num->A` STRIP_ASSUME_TAC) THEN
EXISTS_TAC `\x:A. if ?n:num. ~P(s x n)
then (h:A->B)(@y. ?n. (y = s x n) /\ ~P(s x n) /\
!m. m < n ==> P(s x m))
else something_arbitrary:B` THEN
X_GEN_TAC `x:A` THEN
SUBGOAL_THEN `!n x:A. s (g x) n :A = s x (SUC n)` ASSUME_TAC THENL
[INDUCT_TAC THEN ASM_REWRITE_TAC[];
UNDISCH_THEN `!x:A n. s x (SUC n) :A = g (s x n)` (K ALL_TAC)] THEN
ASM_CASES_TAC `(P:A->bool) x` THEN ASM_REWRITE_TAC[] THENL
[ASM_SIMP_TAC[lemma1] THEN COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN
CONV_TAC SYM_CONV THEN ASM_SIMP_TAC[lemma2; lemma1];
COND_CASES_TAC THENL [ALL_TAC; ASM_MESON_TAC[]] THEN
AP_TERM_TAC THEN MATCH_MP_TAC SELECT_UNIQUE THEN
REWRITE_TAC[] THEN X_GEN_TAC `y:A` THEN EQ_TAC THENL
[SIMP_TAC[LEFT_IMP_EXISTS_THM] THEN
INDUCT_TAC THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[LT_0];
ASM_MESON_TAC[LT]]]);;
(* ------------------------------------------------------------------------- *)
(* A more general mix of tail and wellfounded recursion. *)
(* ------------------------------------------------------------------------- *)
let WF_REC_TAIL_GENERAL = prove
(`!P G H.
WF(<<) /\
(!f g x. (!z. z << x ==> (f z = g z))
==> (P f x <=> P g x) /\ G f x = G g x /\ H f x = H g x) /\
(!f g x. (!z. z << x ==> (f z = g z)) ==> (H f x = H g x)) /\
(!f x y. P f x /\ y << G f x ==> y << x)
==> ?f:A->B. !x. f x = if P f x then f(G f x) else H f x`,
REPEAT STRIP_TAC THEN
CHOOSE_THEN MP_TAC
(prove_inductive_relations_exist
`(!x:A. ~(P f x) ==> terminates f x) /\
(!x. P (f:A->B) x /\ terminates f (G f x) ==> terminates f x)`) THEN
REWRITE_TAC[
FORALL_AND_THM] THEN
DISCH_THEN(STRIP_ASSUME_TAC o GSYM) THEN
SUBGOAL_THEN
`?while. !f:A->B x:A.
while f x = if P f x then
while f (G f x) else x`
(STRIP_ASSUME_TAC o GSYM)
THENL [REWRITE_TAC[GSYM
SKOLEM_THM;
WF_REC_TAIL]; ALL_TAC] THEN
SUBGOAL_THEN
`?f:A->B. !x. f x = if terminates f x then H f (
while f x :A) else anything`
MP_TAC THENL
[FIRST_ASSUM(MATCH_MP_TAC o MATCH_MP
WF_REC) THEN
REPEAT STRIP_TAC THEN MATCH_MP_TAC(MESON[]
`(a = b) /\ (a /\ b ==> (x = y) /\ (f x = g x))
==> ((if a then f x else d) = (if b then g y else d))`) THEN
REPEAT STRIP_TAC THENL
[SUBGOAL_THEN
`!f g x.
(!x y. P f x /\ y << G (f:A->B) x ==> y << x) /\
(!y:A. (!z:A. z << y ==> z << x)
==> (P f y = P g y) /\ (G f y = G g y))
==> terminates f x ==> terminates g x`
(fun th -> EQ_TAC THEN MATCH_MP_TAC th THEN ASM_MESON_TAC[]) THEN
GEN_TAC THEN GEN_TAC THEN
REWRITE_TAC[
RIGHT_FORALL_IMP_THM;
IMP_CONJ] THEN DISCH_TAC THEN
ONCE_REWRITE_TAC[TAUT `a ==> b ==> c <=> b ==> a ==> c`] THEN
FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_MESON_TAC[];
SUBGOAL_THEN
`!x:A. terminates (f:A->B) x /\
(!y:A. (!z:A. z << y ==> z << x)
==> (P f y <=> P g y) /\ (G f y :A = G g y))
==> (
while f x :A =
while g x)`
(fun th -> MATCH_MP_TAC th THEN ASM_MESON_TAC[]) THEN
REWRITE_TAC[
IMP_CONJ] THEN
FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_MESON_TAC[];
FIRST_X_ASSUM MATCH_MP_TAC THEN
SUBGOAL_THEN
`!f:A->B. (!x:A y:A. P f x /\ y << G f x ==> y << x)
==> !x. terminates f x ==> !y. y <<
while f x ==> y << x`
(fun th -> ASM_MESON_TAC[th]) THEN
GEN_TAC THEN DISCH_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN
ASM_MESON_TAC[]];
MATCH_MP_TAC
MONO_EXISTS THEN GEN_TAC THEN
DISCH_THEN(fun th -> ASSUME_TAC(GSYM th) THEN MP_TAC th) THEN
MATCH_MP_TAC
MONO_FORALL THEN X_GEN_TAC `x:A` THEN
ASM_CASES_TAC `P (f:A->B) (x:A) :bool` THEN ASM_MESON_TAC[]]);;
(* ------------------------------------------------------------------------- *)
(* Tactic to apply WF induction on a free "measured" term in the goal. *)
(* ------------------------------------------------------------------------- *)
let WF_INDUCT_TAC =
let qqconv =
let pth = prove
(`(!x. P x ==> !y. Q x y) <=> !y x. P x ==> Q x y`,
MESON_TAC[]) in
GEN_REWRITE_CONV I [pth]
and eqconv =
let pth = prove
(`(!m. P m ==> (m = e) ==> Q) <=> (P e ==> Q)`, MESON_TAC[]) in
REWR_CONV pth in
let rec qqconvs tm =
try (qqconv THENC BINDER_CONV qqconvs) tm
with Failure _ -> eqconv tm in
fun tm (asl,w as gl) ->
let fvs = frees tm
and gv = genvar(type_of tm) in
let pat = list_mk_forall(gv::fvs,mk_imp(mk_eq(gv,tm),w)) in
let th0 = UNDISCH(PART_MATCH rand num_WF pat) in
let th1 = MP (SPECL (tm::fvs) th0) (REFL tm) in
let th2 = CONV_RULE(LAND_CONV qqconvs) (DISCH_ALL th1) in
(MATCH_MP_TAC th2 THEN MAP_EVERY X_GEN_TAC fvs THEN
CONV_TAC(LAND_CONV qqconvs) THEN DISCH_THEN ASSUME_TAC) gl;;