(* ========================================================================= *)
(* Automated support for general recursive definitions.                      *)
(*                                                                           *)
(*              (c) Copyright, John Harrison 1998-2007                       *)
(* ========================================================================= *)

needs "cart.ml";;

(* ------------------------------------------------------------------------- *)
(* Constant supporting casewise definitions.                                 *)
(* ------------------------------------------------------------------------- *)

let CASEWISE_DEF = new_recursive_definition list_RECURSION
 `(CASEWISE [] f x = @y. T) /\
  (CASEWISE (CONS h t) f x =
        if ?y. FST h y = x then SND h f (@y. FST h y = x)
        else CASEWISE t f x)`;;
let CASEWISE = 
prove (`(CASEWISE [] f x = @y. T) /\ (CASEWISE (CONS (s,t) clauses) f x = if ?y. s y = x then t f (@y. s y = x) else CASEWISE clauses f x)`,
REWRITE_TAC[CASEWISE_DEF]);;
(* ------------------------------------------------------------------------- *) (* Conditions for all the clauses in a casewise definition to hold. *) (* ------------------------------------------------------------------------- *)
let CASEWISE_CASES = 
prove (`!clauses c x. (?s t a. MEM (s,t) clauses /\ (s a = x) /\ (CASEWISE clauses c x = t c a)) \/ ~(?s t a. MEM (s,t) clauses /\ (s a = x)) /\ (CASEWISE clauses c x = @y. T)`,
MATCH_MP_TAC list_INDUCT THEN REWRITE_TAC[MEM; CASEWISE; FORALL_PAIR_THM; PAIR_EQ] THEN REPEAT STRIP_TAC THEN COND_CASES_TAC THEN ASM_MESON_TAC[]);;
let CASEWISE_WORKS = 
prove (`!clauses c:C. (!s t s' t' x y. MEM (s,t) clauses /\ MEM (s',t') clauses /\ (s x = s' y) ==> (t c x = t' c y)) ==> ALL (\(s:P->A,t). !x. CASEWISE clauses c (s x) :B = t c x) clauses`,
REWRITE_TAC[GSYM ALL_MEM; FORALL_PAIR_THM] THEN MESON_TAC[CASEWISE_CASES]);;
(* ------------------------------------------------------------------------- *) (* Various notions of admissibility, with tail recursion and preconditions. *) (* ------------------------------------------------------------------------- *)
let admissible = new_definition
 `admissible(<<) p s t <=>
        !f g a. p f a /\ p g a /\ (!z. z << s(a) ==> (f z = g z))
                ==> (t f a = t g a)`;;
let tailadmissible = new_definition
 `tailadmissible(<<) p s t <=>
        ?P G H. (!f a y. P f a /\ y << G f a ==> y << s a) /\
                (!f g a. (!z. z << s(a) ==> (f z = g z))
                         ==> (P f a = P g a) /\
                             (G f a = G g a) /\ (H f a = H g a)) /\
                (!f a:P. p f a ==> (t (f:A->B) a =
                                    if P f a then f(G f a) else H f a))`;;
let superadmissible = new_definition
 `superadmissible(<<) p s t <=>
        admissible(<<) (\f a. T) s p ==> tailadmissible(<<) p s t`;;
(* ------------------------------------------------------------------------- *) (* A lemma. *) (* ------------------------------------------------------------------------- *)
let MATCH_SEQPATTERN = 
prove (`_MATCH x (_SEQPATTERN r s) = if ?y. r x y then _MATCH x r else _MATCH x s`,
REWRITE_TAC[_MATCH; _SEQPATTERN] THEN MESON_TAC[]);;
(* ------------------------------------------------------------------------- *) (* Admissibility combinators. *) (* ------------------------------------------------------------------------- *)
let ADMISSIBLE_CONST = 
prove (`!p s c. admissible(<<) p s (\f. c)`,
REWRITE_TAC[admissible]);;
let ADMISSIBLE_BASE = 
prove (`!(<<) p s t. (!f a. p f a ==> t a << s a) ==> admissible((<<):A->A->bool) p s (\f:A->B x:P. f(t x))`,
REWRITE_TAC[admissible] THEN MESON_TAC[]);;
let ADMISSIBLE_COMB = 
prove (`!(<<) p s:P->A g:(A->B)->P->C->D y:(A->B)->P->C. admissible(<<) p s g /\ admissible(<<) p s y ==> admissible(<<) p s (\f x. (g f x) (y f x))`,
SIMP_TAC[admissible] THEN MESON_TAC[]);;
let ADMISSIBLE_RAND = 
prove (`!(<<) p s:P->A g:P->C->D y:(A->B)->P->C. admissible(<<) p s y ==> admissible(<<) p s (\f x. (g x) (y f x))`,
SIMP_TAC[admissible] THEN MESON_TAC[]);;
let ADMISSIBLE_LAMBDA = 
prove (`!(<<) p s:P->A t:(A->B)->C->P->bool. admissible(<<) (\f (u,x). p f x) (\(u,x). s x) (\f (u,x). t f u x) ==> admissible(<<) p s (\f x. \u. t f u x)`,
REWRITE_TAC[admissible; FUN_EQ_THM; FORALL_PAIR_THM] THEN MESON_TAC[]);;
let ADMISSIBLE_NEST = 
prove (`!(<<) p s t. admissible(<<) p s t /\ (!f a. p f a ==> t f a << s a) ==> admissible((<<):A->A->bool) p s (\f:A->B x:P. f(t f x))`,
REWRITE_TAC[admissible] THEN MESON_TAC[]);;
let ADMISSIBLE_COND = 
prove (`!(<<) p P s h k. admissible(<<) p s P /\ admissible(<<) (\f x. p f x /\ P f x) s h /\ admissible(<<) (\f x. p f x /\ ~P f x) s k ==> admissible(<<) p s (\f x:P. if P f x then h f x else k f x)`,
REPEAT GEN_TAC THEN REWRITE_TAC[admissible; AND_FORALL_THM] THEN REPEAT(MATCH_MP_TAC MONO_FORALL THEN GEN_TAC) THEN DISCH_THEN(fun th -> STRIP_TAC THEN MP_TAC th) THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_MATCH = 
prove (`!(<<) p s e c. admissible(<<) p s e /\ admissible(<<) p s (\f x. c f x (e f x)) ==> admissible(<<) p s (\f x:P. _MATCH (e f x) (c f x))`,
REWRITE_TAC[admissible; _MATCH] THEN REPEAT STRIP_TAC THEN REPEAT COND_CASES_TAC THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_SEQPATTERN = 
prove (`!(<<) p s c1 c2 e. admissible(<<) p s (\f x:P. ?y. c1 f x (e f x) y) /\ admissible(<<) (\f x. p f x /\ ?y. c1 f x (e f x) y) s (\f x. c1 f x (e f x)) /\ admissible(<<) (\f x. p f x /\ ~(?y. c1 f x (e f x) y)) s (\f x. c2 f x (e f x)) ==> admissible(<<) p s (\f x. _SEQPATTERN (c1 f x) (c2 f x) (e f x))`,
REWRITE_TAC[_SEQPATTERN; admissible] THEN MESON_TAC[]);;
let ADMISSIBLE_UNGUARDED_PATTERN = 
prove (`!(<<) p s pat e t y. admissible (<<) p s pat /\ admissible (<<) p s e /\ admissible (<<) (\f x. p f x /\ pat f x = e f x) s t /\ admissible (<<) (\f x. p f x /\ pat f x = e f x) s y ==> admissible(<<) p s (\f x:P. _UNGUARDED_PATTERN (GEQ (pat f x) (e f x)) (GEQ (t f x) (y f x)))`,
REPEAT GEN_TAC THEN REWRITE_TAC[admissible; FORALL_PAIR_THM; _UNGUARDED_PATTERN] THEN REWRITE_TAC[GEQ_DEF] THEN REPEAT STRIP_TAC THEN MATCH_MP_TAC(TAUT `(a <=> a') /\ (a /\ a' ==> (b <=> b')) ==> (a /\ b <=> a' /\ b')`) THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_GUARDED_PATTERN = 
prove (`!(<<) p s pat q e t y. admissible (<<) p s pat /\ admissible (<<) p s e /\ admissible (<<) (\f x. p f x /\ pat f x = e f x /\ q f x) s t /\ admissible (<<) (\f x. p f x /\ pat f x = e f x) s q /\ admissible (<<) (\f x. p f x /\ pat f x = e f x /\ q f x) s y ==> admissible(<<) p s (\f x:P. _GUARDED_PATTERN (GEQ (pat f x) (e f x)) (q f x) (GEQ (t f x) (y f x)))`,
REPEAT GEN_TAC THEN REWRITE_TAC[admissible; FORALL_PAIR_THM; _GUARDED_PATTERN] THEN REWRITE_TAC[GEQ_DEF] THEN REPEAT STRIP_TAC THEN REPEAT(MATCH_MP_TAC(TAUT `(a <=> a') /\ (a /\ a' ==> (b <=> b')) ==> (a /\ b <=> a' /\ b')`) THEN REPEAT STRIP_TAC) THEN TRY(MATCH_MP_TAC(MESON[] `x = x' /\ y = y' ==> (x = y <=> x' = y')`)) THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_NSUM = 
prove (`!(<<) p:(B->C)->P->bool s:P->A h a b. admissible(<<) (\f (k,x). a(x) <= k /\ k <= b(x) /\ p f x) (\(k,x). s x) (\f (k,x). h f x k) ==> admissible(<<) p s (\f x. nsum(a(x)..b(x)) (h f x))`,
REWRITE_TAC[admissible; FORALL_PAIR_THM] THEN REPEAT STRIP_TAC THEN MATCH_MP_TAC NSUM_EQ_NUMSEG THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_SUM = 
prove (`!(<<) p:(B->C)->P->bool s:P->A h a b. admissible(<<) (\f (k,x). a(x) <= k /\ k <= b(x) /\ p f x) (\(k,x). s x) (\f (k,x). h f x k) ==> admissible(<<) p s (\f x. sum(a(x)..b(x)) (h f x))`,
REWRITE_TAC[admissible; FORALL_PAIR_THM] THEN REPEAT STRIP_TAC THEN MATCH_MP_TAC SUM_EQ_NUMSEG THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_MAP = 
prove (`!(<<) p s h l. admissible(<<) p s l /\ admissible (<<) (\f (y,x). p f x /\ MEM y (l f x)) (\(y,x). s x) (\f (y,x). h f x y) ==> admissible (<<) p s (\f:A->B x:P. MAP (h f x) (l f x))`,
REWRITE_TAC[admissible; FORALL_PAIR_THM] THEN REPEAT STRIP_TAC THEN MATCH_MP_TAC(MESON[] `x = y /\ MAP f x = MAP g x ==> MAP f x = MAP g y`) THEN CONJ_TAC THENL [ASM_MESON_TAC[]; ALL_TAC] THEN MATCH_MP_TAC MAP_EQ THEN REWRITE_TAC[GSYM ALL_MEM] THEN REPEAT STRIP_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[FORALL_PAIR_THM] THEN ASM_MESON_TAC[]);;
let ADMISSIBLE_MATCH_SEQPATTERN = 
prove (`!(<<) p s c1 c2 e. admissible(<<) p s (\f x. ?y. c1 f x (e f x) y) /\ admissible(<<) (\f x. p f x /\ ?y. c1 f x (e f x) y) s (\f x. _MATCH (e f x) (c1 f x)) /\ admissible(<<) (\f x. p f x /\ ~(?y. c1 f x (e f x) y)) s (\f x. _MATCH (e f x) (c2 f x)) ==> admissible(<<) p s (\f x:P. _MATCH (e f x) (_SEQPATTERN (c1 f x) (c2 f x)))`,
(* ------------------------------------------------------------------------- *) (* Superadmissible generalizations where applicable. *) (* *) (* Note that we can't take the "higher type" route in the simple theorem *) (* ADMISSIBLE_MATCH because that isn't a context where tail recursion makes *) (* sense. Instead, we use specific theorems for the two _MATCH instances. *) (* Note that also, because of some delicacy over assessing welldefinedness *) (* of patterns, a special well-formedness hypothesis crops up here. (We need *) (* to separate it from the function f or we lose the "tail" optimization.) *) (* ------------------------------------------------------------------------- *)
let ADMISSIBLE_IMP_SUPERADMISSIBLE = 
prove (`!(<<) p s t:(A->B)->P->B. admissible(<<) p s t ==> superadmissible(<<) p s t`,
REWRITE_TAC[admissible; superadmissible; tailadmissible] THEN REPEAT STRIP_TAC THEN MAP_EVERY EXISTS_TAC [`\f:A->B x:P. F`; `\f:A->B. (anything:P->A)`; `\f:A->B a:P. if p f a then t f a :B else fixed`] THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[]);;
let SUPERADMISSIBLE_CONST = 
prove (`!p s c. superadmissible(<<) p s (\f. c)`,
REPEAT GEN_TAC THEN MATCH_MP_TAC ADMISSIBLE_IMP_SUPERADMISSIBLE THEN REWRITE_TAC[ADMISSIBLE_CONST]);;
let SUPERADMISSIBLE_TAIL = 
prove (`!(<<) p s t:(A->B)->P->A. admissible(<<) p s t /\ (!f a. p f a ==> !y. y << t f a ==> y << s a) ==> superadmissible(<<) p s (\f x. f(t f x))`,
REWRITE_TAC[admissible; superadmissible; tailadmissible] THEN REPEAT STRIP_TAC THEN MAP_EVERY EXISTS_TAC [`\f:A->B x:P. T`; `\f:A->B a:P. if p f a then t f a :A else s a`; `\f:A->B. anything:P->B`] THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[]);;
let SUPERADMISSIBLE_COND = 
prove (`!(<<) p P s h k:(A->B)->P->B. admissible(<<) p s P /\ superadmissible(<<) (\f x. p f x /\ P f x) s h /\ superadmissible(<<) (\f x. p f x /\ ~P f x) s k ==> superadmissible(<<) p s (\f x. if P f x then h f x else k f x)`,
REWRITE_TAC[superadmissible; admissible] THEN REPEAT GEN_TAC THEN DISCH_THEN(CONJUNCTS_THEN2 ASSUME_TAC MP_TAC) THEN DISCH_THEN(fun th -> DISCH_TAC THEN CONJUNCTS_THEN MP_TAC th) THEN ANTS_TAC THENL [ASM_MESON_TAC[]; ALL_TAC] THEN DISCH_THEN(fun th -> ANTS_TAC THENL [ASM_MESON_TAC[]; MP_TAC th]) THEN REWRITE_TAC[tailadmissible] THEN REWRITE_TAC[LEFT_IMP_EXISTS_THM; RIGHT_IMP_FORALL_THM] THEN MAP_EVERY X_GEN_TAC [`P1:(A->B)->P->bool`; `G1:(A->B)->P->A`; `H1:(A->B)->P->B`; `P2:(A->B)->P->bool`; `G2:(A->B)->P->A`; `H2:(A->B)->P->B`] THEN REWRITE_TAC[TAUT `(a1 /\ b1 /\ c1 ==> a2 /\ b2 /\ c2 ==> x) <=> (a1 /\ a2) /\ (b1 /\ b2) /\ (c1 /\ c2) ==> x`] THEN DISCH_THEN(fun th -> MAP_EVERY EXISTS_TAC [`\f:A->B a:P. if p f a then if P f a then P2 f a else P1 f a else F`; `\f:A->B a:P. if p f a then if P f a then G2 f a else G1 f a else z:A`; `\f:A->B a:P. if p f a then if P f a then H2 f a else H1 f a else w:B`] THEN MP_TAC th) THEN REWRITE_TAC[] THEN REPEAT(MATCH_MP_TAC MONO_AND THEN CONJ_TAC) THENL [ASM_MESON_TAC[]; POP_ASSUM_LIST(MP_TAC o end_itlist CONJ); ALL_TAC] THEN REWRITE_TAC[IMP_IMP; AND_FORALL_THM] THEN REPEAT(MATCH_MP_TAC MONO_FORALL THEN GEN_TAC) THEN DISCH_THEN(fun th -> DISCH_TAC THEN MP_TAC th) THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[]);;
let SUPERADMISSIBLE_MATCH_SEQPATTERN = 
prove (`!(<<) p s c1 c2 e. admissible(<<) p s (\f x. ?y. c1 f x (e f x) y) /\ superadmissible(<<) (\f x. p f x /\ ?y. c1 f x (e f x) y) s (\f x. _MATCH (e f x) (c1 f x)) /\ superadmissible(<<) (\f x. p f x /\ ~(?y. c1 f x (e f x) y)) s (\f x. _MATCH (e f x) (c2 f x)) ==> superadmissible(<<) p s (\f x:P. _MATCH (e f x) (_SEQPATTERN (c1 f x) (c2 f x)))`,
let SUPERADMISSIBLE_MATCH_UNGUARDED_PATTERN = 
prove (`!(<<) p s e:P->D pat:Q->D arg. (!f a t u. p f a /\ pat t = e a /\ pat u = e a ==> arg a t = arg a u) /\ (!f a t. p f a /\ pat t = e a ==> !y. y << arg a t ==> y << s a) ==> superadmissible(<<) p s (\f:A->B x. _MATCH (e x) (\u v. ?t. _UNGUARDED_PATTERN (GEQ (pat t) u) (GEQ (f(arg x t)) v)))`,
REPEAT GEN_TAC THEN STRIP_TAC THEN REWRITE_TAC[superadmissible] THEN DISCH_TAC THEN REWRITE_TAC[_UNGUARDED_PATTERN; GEQ_DEF; _MATCH] THEN REWRITE_TAC[tailadmissible] THEN SUBGOAL_THEN `!f:A->B x:P. p f x ==> ((?!v. ?t:Q. pat t:D = e x /\ f(arg x t) = v) <=> ?t. pat t = e x)` (fun th -> SIMP_TAC[th]) THENL [ASM_MESON_TAC[]; ALL_TAC] THEN MAP_EVERY EXISTS_TAC [`\(f:A->B) x:P. p f x /\ ?t:Q. pat t:D = e x`; `\f:A->B x:P. arg x (@t. (pat:Q->D) t = e x):A`; `\(f:A->B) x:P. (@z:B. F)`] THEN RULE_ASSUM_TAC(REWRITE_RULE[admissible]) THEN SIMP_TAC[] THEN ASM_MESON_TAC[]);;
let SUPERADMISSIBLE_MATCH_GUARDED_PATTERN = 
prove (`!(<<) p s e:P->D pat:Q->D q arg. (!f a t u. p f a /\ pat t = e a /\ q a t /\ pat u = e a /\ q a u ==> arg a t = arg a u) /\ (!f a t. p f a /\ q a t /\ pat t = e a ==> !y. y << arg a t ==> y << s a) ==> superadmissible(<<) p s (\f:A->B x. _MATCH (e x) (\u v. ?t. _GUARDED_PATTERN (GEQ (pat t) u) (q x t) (GEQ (f(arg x t)) v)))`,
REPEAT GEN_TAC THEN STRIP_TAC THEN REWRITE_TAC[superadmissible] THEN DISCH_TAC THEN REWRITE_TAC[_GUARDED_PATTERN; GEQ_DEF; _MATCH] THEN REWRITE_TAC[tailadmissible] THEN SUBGOAL_THEN `!f:A->B x:P. p f x ==> ((?!v. ?t:Q. pat t:D = e x /\ q x t /\ f(arg x t) = v) <=> ?t. pat t = e x /\ q x t)` (fun th -> SIMP_TAC[th]) THENL [ASM_MESON_TAC[]; ALL_TAC] THEN MAP_EVERY EXISTS_TAC [`\(f:A->B) x:P. p f x /\ ?t:Q. pat t:D = e x /\ q x t`; `\f:A->B x:P. arg x (@t. (pat:Q->D) t = e x /\ q x t):A`; `\(f:A->B) x:P. (@z:B. F)`] THEN RULE_ASSUM_TAC(REWRITE_RULE[admissible]) THEN SIMP_TAC[] THEN ASM_MESON_TAC[]);;
(* ------------------------------------------------------------------------- *) (* Combine general WF/tail recursion theorem with casewise definitions. *) (* ------------------------------------------------------------------------- *)
let WF_REC_TAIL_GENERAL' = 
prove (`!P G H 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 x y. P f x /\ y << G f x ==> y << x) /\ (!f x. H f x = if P f x then f(G f x) else H' f x) ==> ?f. !x. f x = H f x`,
REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN MATCH_MP_TAC WF_REC_TAIL_GENERAL THEN ASM_MESON_TAC[]);;
let WF_REC_CASES = 
prove (`!(<<) clauses. WF((<<):A->A->bool) /\ ALL (\(s,t). ?P G H. (!f a y. P f a /\ y << G f a ==> y << s a) /\ (!f g a. (!z. z << s(a) ==> (f z = g z)) ==> (P f a = P g a) /\ (G f a = G g a) /\ (H f a = H g a)) /\ (!f a:P. t f a = if P f a then f(G f a) else H f a)) clauses ==> ?f:A->B. !x. f x = CASEWISE clauses f x`,
REPEAT STRIP_TAC THEN MATCH_MP_TAC WF_REC_TAIL_GENERAL' THEN FIRST_X_ASSUM(MP_TAC o check(is_binary "ALL" o concl)) THEN SPEC_TAC(`clauses:((P->A)#((A->B)->P->B))list`, `clauses:((P->A)#((A->B)->P->B))list`) THEN ASM_REWRITE_TAC[] THEN POP_ASSUM(K ALL_TAC) THEN MATCH_MP_TAC list_INDUCT THEN REWRITE_TAC[ALL; CASEWISE; FORALL_PAIR_THM] THEN CONJ_TAC THENL [MAP_EVERY EXISTS_TAC [`\f:A->B x:A. F`; `\f:A->B. anything:A->A`; `\f:A->B x:A. @y:B. T`] THEN REWRITE_TAC[]; ALL_TAC] THEN MAP_EVERY X_GEN_TAC [`s:P->A`; `t:(A->B)->P->B`; `clauses:((P->A)#((A->B)->P->B))list`] THEN DISCH_THEN(fun th -> DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC) THEN MP_TAC th) THEN ASM_REWRITE_TAC[] THEN POP_ASSUM_LIST(K ALL_TAC) THEN REWRITE_TAC[LEFT_IMP_EXISTS_THM] THEN REWRITE_TAC[RIGHT_IMP_FORALL_THM] THEN MAP_EVERY X_GEN_TAC [`P1:(A->B)->A->bool`; `G1:(A->B)->A->A`; `H1:(A->B)->A->B`; `P2:(A->B)->P->bool`; `G2:(A->B)->P->A`; `H2:(A->B)->P->B`] THEN REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN EXISTS_TAC `\f:A->B x:A. if ?y:P. s y = x then P2 f (@y. s y = x) else P1 f x:bool` THEN EXISTS_TAC `\f:A->B x:A. if ?y:P. s y = x then G2 f (@y. s y = x) else G1 f x:A` THEN EXISTS_TAC `\f:A->B x:A. if ?y:P. s y = x then H2 f (@y. s y = x) else H1 f x:B` THEN ASM_MESON_TAC[]);;
let WF_REC_CASES' = 
prove (`!(<<) clauses. WF((<<):A->A->bool) /\ ALL (\(s,t). tailadmissible(<<) (\f a. T) s t) clauses ==> ?f:A->B. !x. f x = CASEWISE clauses f x`,
REWRITE_TAC[WF_REC_CASES; tailadmissible]);;
let RECURSION_CASEWISE = 
prove (`!clauses. (?(<<). WF(<<) /\ ALL (\(s:P->A,t). tailadmissible(<<) (\f a. T) s t) clauses) /\ (!s t s' t' f x y. MEM (s,t) clauses /\ MEM (s',t') clauses ==> (s x = s' y) ==> (t f x = t' f y)) ==> ?f:A->B. ALL (\(s,t). !x. f (s x) = t f x) clauses`,
REPEAT GEN_TAC THEN REWRITE_TAC[IMP_IMP; CONJ_ASSOC] THEN DISCH_THEN(CONJUNCTS_THEN2 MP_TAC ASSUME_TAC) THEN DISCH_THEN(CHOOSE_THEN (MP_TAC o MATCH_MP WF_REC_CASES')) THEN MATCH_MP_TAC MONO_EXISTS THEN REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN ASM_MESON_TAC[CASEWISE_WORKS]);;
let RECURSION_CASEWISE_PAIRWISE = 
prove (`!clauses. (?(<<). WF (<<) /\ ALL (\(s,t). tailadmissible(<<) (\f a. T) s t) clauses) /\ ALL (\(s,t). !f x y. (s x = s y) ==> (t f x = t f y)) clauses /\ PAIRWISE (\(s,t) (s',t'). !f x y. (s x = s' y) ==> (t f x = t' f y)) clauses ==> (?f. ALL (\(s,t). !x. f (s x) = t f x) clauses)`,
let lemma = prove
   (`!P. (!x y. P x y ==> P y x)
         ==> !l. (!x y. MEM x l /\ MEM y l ==> P x y) <=>
                 ALL (\x. P x x) l /\ PAIRWISE P l`,
    REWRITE_TAC[IMP_CONJ; RIGHT_FORALL_IMP_THM; GSYM ALL_MEM] THEN
    REPEAT GEN_TAC THEN DISCH_TAC THEN LIST_INDUCT_TAC THEN
    REWRITE_TAC[PAIRWISE; MEM; GSYM ALL_MEM] THEN ASM_MESON_TAC[])
  and paired_lambda = prove
   (`(\x. P x) = (\(a,b). P (a,b))`,
    REWRITE_TAC[FUN_EQ_THM; FORALL_PAIR_THM]) in
  let pth = REWRITE_RULE[FORALL_PAIR_THM; paired_lambda] (ISPEC
    `\(s,t) (s',t'). !c x:A y:A. (s x = s' y) ==> (t c x = t' c y)` lemma) in
  let cth = prove(lhand(concl pth),MESON_TAC[]) in
  REWRITE_TAC[GSYM(MATCH_MP pth cth); RIGHT_IMP_FORALL_THM] THEN
  REWRITE_TAC[RECURSION_CASEWISE]);;
let SUPERADMISSIBLE_T = 
prove (`superadmissible(<<) (\f x. T) s t <=> tailadmissible(<<) (\f x. T) s t`,
REWRITE_TAC[superadmissible; admissible]);;
let RECURSION_SUPERADMISSIBLE = REWRITE_RULE[GSYM SUPERADMISSIBLE_T] RECURSION_CASEWISE_PAIRWISE;; (* ------------------------------------------------------------------------- *) (* The main suite of functions for justifying recursion. *) (* ------------------------------------------------------------------------- *) let instantiate_casewise_recursion, pure_prove_recursive_function_exists, prove_general_recursive_function_exists = (* ------------------------------------------------------------------------- *) (* Make some basic simplification of conjunction of welldefinedness clauses. *) (* ------------------------------------------------------------------------- *) let SIMPLIFY_WELLDEFINEDNESS_CONV = let LSYM = GEN_ALL o CONV_RULE(LAND_CONV(ONCE_DEPTH_CONV SYM_CONV)) o SPEC_ALL and evensimps = prove (`((2 * m + 2 = 2 * n + 1) <=> F) /\ ((2 * m + 1 = 2 * n + 2) <=> F) /\ ((2 * m = 2 * n + 1) <=> F) /\ ((2 * m + 1 = 2 * n) <=> F) /\ ((2 * m = SUC(2 * n)) <=> F) /\ ((SUC(2 * m) = 2 * n) <=> F)`, REWRITE_TAC[] THEN REPEAT CONJ_TAC THEN DISCH_THEN(MP_TAC o AP_TERM `EVEN`) THEN REWRITE_TAC[EVEN_MULT; EVEN_ADD; ARITH; EVEN]) in let allsimps = itlist (mk_rewrites false) [EQ_ADD_RCANCEL; EQ_ADD_LCANCEL; EQ_ADD_RCANCEL_0; EQ_ADD_LCANCEL_0; LSYM EQ_ADD_RCANCEL_0; LSYM EQ_ADD_LCANCEL_0; EQ_MULT_RCANCEL; EQ_MULT_LCANCEL; EQT_INTRO(SPEC_ALL EQ_REFL); ADD_EQ_0; LSYM ADD_EQ_0; MULT_EQ_0; LSYM MULT_EQ_0; MULT_EQ_1; LSYM MULT_EQ_1; ARITH_RULE `(m + n = 1) <=> (m = 1) /\ (n = 0) \/ (m = 0) /\ (n = 1)`; ARITH_RULE `(1 = m + n) <=> (m = 1) /\ (n = 0) \/ (m = 0) /\ (n = 1)`; evensimps; ARITH_EQ] [] and [simp1; simp2; simp3] = map MATCH_MP (CONJUNCTS (TAUT `((a <=> F) /\ (b <=> b) ==> ((a ==> b) <=> T)) /\ ((a <=> a') /\ (a' ==> (b <=> T)) ==> ((a ==> b) <=> T)) /\ ((a <=> a') /\ (a' ==> (b <=> b')) ==> ((a ==> b) <=> (a' ==> b')))`)) and false_tm = `F` and and_tm = `(/\)` and eq_refl = EQT_INTRO(SPEC_ALL EQ_REFL) in fun tm -> let net = itlist (net_of_thm false) allsimps (!basic_rectype_net) in let RECTYPE_ARITH_EQ_CONV = TOP_SWEEP_CONV(REWRITES_CONV net) THENC GEN_REWRITE_CONV DEPTH_CONV [AND_CLAUSES; OR_CLAUSES] in let SIMPLIFY_CASE_DISTINCTNESS_CLAUSE tm = let avs,bod = strip_forall tm in let ant,cons = dest_imp bod in let ath = RECTYPE_ARITH_EQ_CONV ant in let atm = rand(concl ath) in let bth = CONJ ath (if atm = false_tm then REFL cons else DISCH atm (PURE_REWRITE_CONV[eq_refl; ASSUME atm] cons)) in let cth = try simp1 bth with Failure _ -> try simp2 bth with Failure _ -> simp3 bth in itlist MK_FORALL avs cth in (DEPTH_BINOP_CONV and_tm SIMPLIFY_CASE_DISTINCTNESS_CLAUSE THENC GEN_REWRITE_CONV DEPTH_CONV [FORALL_SIMP; AND_CLAUSES]) tm in (* ------------------------------------------------------------------------- *) (* Simplify an existential question about a pattern. *) (* ------------------------------------------------------------------------- *) let EXISTS_PAT_CONV =
let pth = 
prove (`((?y. _UNGUARDED_PATTERN (GEQ s t) (GEQ z y)) <=> s = t) /\ ((?y. _GUARDED_PATTERN (GEQ s t) g (GEQ z y)) <=> g /\ s = t)`,
REWRITE_TAC[_UNGUARDED_PATTERN; _GUARDED_PATTERN; GEQ_DEF] THEN MESON_TAC[]) in let basecnv = GEN_REWRITE_CONV I [pth] and pushcnv = GEN_REWRITE_CONV I [SWAP_EXISTS_THM] in let rec EXISTS_PAT_CONV tm = ((pushcnv THENC BINDER_CONV EXISTS_PAT_CONV) ORELSEC basecnv) tm in fun tm -> if is_exists tm then EXISTS_PAT_CONV tm else failwith "EXISTS_PAT_CONV" in (* ------------------------------------------------------------------------- *) (* Hack a proforma to introduce new pairing or pattern variables. *) (* ------------------------------------------------------------------------- *) let HACK_PROFORMA,EACK_PROFORMA =
let elemma0 = prove
     (`((!z. GEQ (f z) (g z)) <=> (!x y. GEQ (f(x,y)) (g(x,y)))) /\
       ((\p. P p) = (\(x,y). P(x,y)))`,
      REWRITE_TAC[FUN_EQ_THM; FORALL_PAIR_THM])
    and elemma1 = prove
     (`(!P. (!t:A->B->C#D->E. P t) <=> (!t. P (\a b (c,d). t a b d c))) /\
       (!P. (!t:B->C#D->E. P t) <=> (!t. P (\b (c,d). t b d c))) /\
       (!P. (!t:C#D->E. P t) <=> (!t. P (\(c,d). t d c)))`,
      REPEAT STRIP_TAC THEN EQ_TAC THEN REPEAT STRIP_TAC THEN
      ASM_REWRITE_TAC[] THENL
       [FIRST_X_ASSUM(MP_TAC o SPEC `\a b d c. (t:A->B->C#D->E) a b (c,d)`);
        FIRST_X_ASSUM(MP_TAC o SPEC `\b d c. (t:B->C#D->E) b (c,d)`);
        FIRST_X_ASSUM(MP_TAC o SPEC `\d c. (t:C#D->E) (c,d)`)] THEN
      MATCH_MP_TAC EQ_IMP THEN AP_TERM_TAC THEN
      REWRITE_TAC[FUN_EQ_THM; FORALL_PAIR_THM]) in
    let HACK_PROFORMA n th =
      if n <= 1 then th else
      let mkname i = "_P"^string_of_int i in
      let ty = end_itlist (fun s t -> mk_type("prod",[s;t]))
                          (map (mk_vartype o mkname) (1--n)) in
      let conv i =
        let name = "x"^string_of_int i in
        let cnv = ALPHA_CONV (mk_var(name,mk_vartype(mkname i))) in
        fun tm -> if is_abs tm & name_of(bndvar tm) <> name
                  then cnv tm else failwith "conv" in
      let convs = FIRST_CONV (map conv (1--n)) in
      let th1 = INST_TYPE [ty,`:P`] th in
      let th2 = REWRITE_RULE[FORALL_PAIR_THM] th1 in
      let th3 = REWRITE_RULE[elemma0; elemma1] th2 in
      CONV_RULE(REDEPTH_CONV convs) th3
    and EACK_PROFORMA n th =
      if n <= 1 then th else
      let mkname i = "_Q"^string_of_int i in
      let ty = end_itlist (fun s t -> mk_type("prod",[s;t]))
                          (map (mk_vartype o mkname) (1--n)) in
      let conv i =
        let name = "t"^string_of_int i in
        let cnv = ALPHA_CONV (mk_var(name,mk_vartype(mkname i))) in
        fun tm -> if is_abs tm & name_of(bndvar tm) <> name
                  then cnv tm else failwith "conv" in
      let convs = FIRST_CONV (map conv (1--n)) in
      let th1 = INST_TYPE [ty,`:Q`] th in
      let th2 = REWRITE_RULE[EXISTS_PAIR_THM] th1 in
      let th3 = REWRITE_RULE[elemma1] th2 in
      let th4 = REWRITE_RULE[FORALL_PAIR_THM] th3 in
      CONV_RULE(REDEPTH_CONV convs) th4 in
    HACK_PROFORMA,EACK_PROFORMA in

(* ------------------------------------------------------------------------- *)
(* Hack and apply.                                                           *)
(* ------------------------------------------------------------------------- *)

  let APPLY_PROFORMA_TAC th (asl,w as gl) =
    let vs = fst(dest_gabs(body(rand w))) in
    let n = 1 + length(fst(splitlist dest_pair vs)) in
    (MATCH_MP_TAC(HACK_PROFORMA n th) THEN BETA_TAC) gl in

  let is_pattern p n tm =
    try let f,args = strip_comb(snd(strip_exists (body(body tm)))) in
        is_const f & name_of f = p & length args = n
    with Failure _ -> false in

  let SIMPLIFY_MATCH_WELLDEFINED_TAC =
    let pth0 = MESON[]
     `(a /\ x = k ==> x = y ==> d) ==> (a /\ x = k /\ y = k ==> d)`
    and pth1 = MESON[]
     `(a /\ b /\ c /\ x = k ==> x = y ==> d)
      ==> (a /\ x = k /\ b /\ y = k /\ c ==> d)` in
    REPEAT GEN_TAC THEN
    (MATCH_MP_TAC pth1 ORELSE MATCH_MP_TAC pth0) THEN
    CONV_TAC(RAND_CONV SIMPLIFY_WELLDEFINEDNESS_CONV) THEN
    PURE_REWRITE_TAC
      [AND_CLAUSES; IMP_CLAUSES; OR_CLAUSES; EQ_CLAUSES; NOT_CLAUSES] in

  let rec headonly f tm =
    match tm with
      Comb(s,t) -> headonly f s & headonly f t & not(t = f)
    | Abs(x,t) -> headonly f t
    | _ -> true in

  let MAIN_ADMISS_TAC (asl,w as gl) =
    let had,args = strip_comb w in
    if not(is_const had) then failwith "ADMISS_TAC" else
    let f,fbod = dest_abs(last args) in
    let xtup,bod = dest_gabs fbod in
    let hop,args = strip_comb bod in
    match (name_of had,name_of hop) with
      "superadmissible","COND"
          -> APPLY_PROFORMA_TAC SUPERADMISSIBLE_COND gl
    | "superadmissible","_MATCH" when
          name_of(repeat rator (last args)) = "_SEQPATTERN"
          -> (APPLY_PROFORMA_TAC SUPERADMISSIBLE_MATCH_SEQPATTERN THEN
              CONV_TAC(ONCE_DEPTH_CONV EXISTS_PAT_CONV)) gl
    | "superadmissible","_MATCH" when
         is_pattern "_UNGUARDED_PATTERN" 2 (last args)
          -> let n = length(fst(strip_exists(body(body(last args))))) in
             let th = EACK_PROFORMA n SUPERADMISSIBLE_MATCH_UNGUARDED_PATTERN in
             (APPLY_PROFORMA_TAC th THEN CONJ_TAC THENL
               [SIMPLIFY_MATCH_WELLDEFINED_TAC; ALL_TAC]) gl
    | "superadmissible","_MATCH" when
         is_pattern "_GUARDED_PATTERN" 3 (last args)
          -> let n = length(fst(strip_exists(body(body(last args))))) in
             let th = EACK_PROFORMA n SUPERADMISSIBLE_MATCH_GUARDED_PATTERN in
             (APPLY_PROFORMA_TAC th THEN CONJ_TAC THENL
               [SIMPLIFY_MATCH_WELLDEFINED_TAC; ALL_TAC]) gl
    | "superadmissible",_ when is_comb bod & rator bod = f
          -> APPLY_PROFORMA_TAC SUPERADMISSIBLE_TAIL gl
    | "admissible","sum"
          -> APPLY_PROFORMA_TAC ADMISSIBLE_SUM gl
    | "admissible","nsum"
          -> APPLY_PROFORMA_TAC ADMISSIBLE_NSUM gl
    | "admissible","MAP"
          -> APPLY_PROFORMA_TAC ADMISSIBLE_MAP gl
    | "admissible","_MATCH" when
          name_of(repeat rator (last args)) = "_SEQPATTERN"
          -> (APPLY_PROFORMA_TAC ADMISSIBLE_MATCH_SEQPATTERN THEN
              CONV_TAC(ONCE_DEPTH_CONV EXISTS_PAT_CONV)) gl
    | "admissible","_MATCH"
          -> APPLY_PROFORMA_TAC ADMISSIBLE_MATCH gl
    | "admissible","_UNGUARDED_PATTERN"
          -> APPLY_PROFORMA_TAC ADMISSIBLE_UNGUARDED_PATTERN gl
    | "admissible","_GUARDED_PATTERN"
          -> APPLY_PROFORMA_TAC ADMISSIBLE_GUARDED_PATTERN gl
    | "admissible",_ when is_abs bod
          -> APPLY_PROFORMA_TAC ADMISSIBLE_LAMBDA gl
    | "admissible",_ when is_comb bod & rator bod = f
          -> if free_in f (rand bod) then
               APPLY_PROFORMA_TAC ADMISSIBLE_NEST gl
             else
               APPLY_PROFORMA_TAC ADMISSIBLE_BASE gl
    | "admissible",_ when is_comb bod & headonly f bod
          -> APPLY_PROFORMA_TAC ADMISSIBLE_COMB gl
    | _ -> failwith "MAIN_ADMISS_TAC" in

  let ADMISS_TAC =
    CONJ_TAC ORELSE
    MATCH_ACCEPT_TAC ADMISSIBLE_CONST ORELSE
    MATCH_ACCEPT_TAC SUPERADMISSIBLE_CONST ORELSE
    MAIN_ADMISS_TAC ORELSE
    MATCH_MP_TAC ADMISSIBLE_IMP_SUPERADMISSIBLE in

(* ------------------------------------------------------------------------- *)
(* Instantiate the casewise recursion theorem for existential claim.         *)
(* Also make a first attempt to simplify the distinctness clause. This may   *)
(* yield a theorem with just the wellfoundedness "?(<<)" assumption, or it   *)
(* may be that and an additional distinctness one.                           *)
(* ------------------------------------------------------------------------- *)

  let instantiate_casewise_recursion =
    let EXPAND_PAIRED_ALL_CONV =
      let pth0,pth1 = (CONJ_PAIR o prove)
       (`(ALL (\(s,t). P s t) [a,b] <=> P a b) /\
         (ALL (\(s,t). P s t) (CONS (a,b) l) <=>
          P a b /\ ALL (\(s,t). P s t) l)`,
        REWRITE_TAC[ALL]) in
      let conv0 = REWR_CONV pth0 and conv1 = REWR_CONV pth1 in
      let rec conv tm =
        try conv0 tm with Failure _ ->
        let th = conv1 tm in CONV_RULE (funpow 2 RAND_CONV conv) th in
      conv
    and LAMBDA_PAIR_CONV =
      let rewr1 =  GEN_REWRITE_RULE I [GSYM FORALL_PAIR_THM]
      and rewr2 = GEN_REWRITE_CONV I [FUN_EQ_THM] in
      fun parms tm ->
        let parm = end_itlist (curry mk_pair) parms in
        let x,bod = dest_abs tm in
        let tm' = mk_gabs(parm,vsubst[parm,x] bod) in
        let th1 = BETA_CONV(mk_comb(tm,parm))
        and th2 = GEN_BETA_CONV (mk_comb(tm',parm)) in
        let th3 = TRANS th1 (SYM th2) in
        let th4 = itlist (fun v th -> rewr1 (GEN v th))
                         (butlast parms) (GEN (last parms) th3) in
        EQ_MP (SYM(rewr2(mk_eq(tm,tm')))) th4
    and FORALL_PAIR_CONV =
      let rule = GEN_REWRITE_RULE RAND_CONV [GSYM FORALL_PAIR_THM] in
      let rec depair l t =
        match l with
          [v] -> REFL t
        | v::vs -> rule(BINDER_CONV (depair vs) t) in
      fun parm parms ->
        let p = mk_var("P",mk_fun_ty (type_of parm) bool_ty) in
        let tm = list_mk_forall(parms,mk_comb(p,parm)) in
        GEN p (SYM(depair parms tm)) in
    let ELIM_LISTOPS_CONV =
      PURE_REWRITE_CONV[PAIRWISE; ALL; GSYM CONJ_ASSOC; AND_CLAUSES] THENC
      TOP_DEPTH_CONV GEN_BETA_CONV in
    let tuple_function_existence tm =
      let f,def = dest_exists tm in
      let domtys0,ranty0 = splitlist dest_fun_ty (type_of f) in
      let nargs =
        itlist
         (max o length o snd o strip_comb o lhs o snd o strip_forall)
         (conjuncts(snd(strip_forall def))) 0 in
      let domtys,midtys = chop_list nargs domtys0 in
      let ranty = itlist mk_fun_ty midtys ranty0 in
      if length domtys <= 1 then ASSUME tm else
      let dty = end_itlist (fun ty1 ty2 -> mk_type("prod",[ty1;ty2])) domtys in
      let f' = variant (frees tm)
                       (mk_var(fst(dest_var f),mk_fun_ty dty ranty)) in
      let gvs = map genvar domtys in
      let f'' = list_mk_abs(gvs,mk_comb(f',end_itlist (curry mk_pair) gvs)) in
      let def' = subst [f'',f] def in
      let th1 = EXISTS (tm,f'') (ASSUME def')
      and bth = BETAS_CONV (list_mk_comb(f'',gvs)) in
      let th2 = GEN_REWRITE_CONV TOP_DEPTH_CONV [bth] (hd(hyp th1)) in
      SIMPLE_CHOOSE f' (PROVE_HYP (UNDISCH(snd(EQ_IMP_RULE th2))) th1) in
    let pinstantiate_casewise_recursion def =
      try PART_MATCH I EXISTS_REFL def with Failure _ ->
      let f,bod = dest_exists def in
      let cjs = conjuncts bod in
      let eqs = map (snd o strip_forall) cjs in
      let lefts,rights = unzip(map dest_eq eqs) in
      let arglists = map (snd o strip_comb) lefts in
      let parms0 = freesl(unions arglists) in
      let parms = if parms0 <> [] then parms0 else [genvar aty] in
      let parm = end_itlist (curry mk_pair) parms in
      let ss = map (fun a -> mk_gabs(parm,end_itlist (curry mk_pair) a))
                   arglists
      and ts = map (fun a -> mk_abs(f,mk_gabs(parm,a))) rights in
      let clauses = mk_flist(map2 (curry mk_pair) ss ts) in
      let pth = ISPEC clauses RECURSION_SUPERADMISSIBLE in
      let FIDDLE_CONV =
        (LAND_CONV o LAND_CONV o BINDER_CONV o RAND_CONV o LAND_CONV o
         GABS_CONV o RATOR_CONV o LAND_CONV o ABS_CONV) in
      let th0 = UNDISCH(CONV_RULE(FIDDLE_CONV(LAMBDA_PAIR_CONV parms)) pth) in
      let th1 = EQ_MP (GEN_ALPHA_CONV f (concl th0)) th0 in
      let rewr_forall_th = REWR_CONV(FORALL_PAIR_CONV parm parms) in
      let th2 = CONV_RULE (BINDER_CONV
                    (LAND_CONV(GABS_CONV rewr_forall_th) THENC
                     EXPAND_PAIRED_ALL_CONV)) th1 in
      let f2,bod2 = dest_exists(concl th2) in
      let ths3 = map
       (CONV_RULE (COMB2_CONV (funpow 2 RAND_CONV GEN_BETA_CONV)
                  (RATOR_CONV BETA_CONV THENC GEN_BETA_CONV)) o SPEC_ALL)
       (CONJUNCTS(ASSUME bod2)) in
      let ths4 = map2
       (fun th t -> let avs,tbod = strip_forall t in
                    itlist GEN avs (PART_MATCH I th tbod)) ths3 cjs in
      let th5 = SIMPLE_EXISTS f (end_itlist CONJ ths4) in
      let th6 = PROVE_HYP th2 (SIMPLE_CHOOSE f th5) in
      let th7 =
       (RAND_CONV(COMB2_CONV
            (RAND_CONV (LAND_CONV (GABS_CONV(BINDER_CONV
                     (BINDER_CONV(rewr_forall_th) THENC rewr_forall_th)))))
            (LAND_CONV (funpow 2 GABS_CONV(BINDER_CONV
                     (BINDER_CONV(rewr_forall_th) THENC
                      rewr_forall_th))))) THENC
        ELIM_LISTOPS_CONV) (hd(hyp th6)) in
      let th8 = PROVE_HYP (UNDISCH(snd(EQ_IMP_RULE th7))) th6 in
      let wfasm,cdasm = dest_conj(hd(hyp th8)) in
      let th9 = PROVE_HYP (CONJ (ASSUME wfasm) (ASSUME cdasm)) th8 in
      let th10 = SIMPLIFY_WELLDEFINEDNESS_CONV cdasm in
      let th11 = PROVE_HYP (UNDISCH(snd(EQ_IMP_RULE th10))) th9 in
      PROVE_HYP TRUTH th11 in
    fun etm ->
      let eth = tuple_function_existence etm in
      let dtm = hd(hyp eth) in
      let dth = pinstantiate_casewise_recursion dtm in
      PROVE_HYP dth eth in

(* ------------------------------------------------------------------------- *)
(* Justify existence assertion and try to simplify/remove side-conditions.   *)
(* ------------------------------------------------------------------------- *)

  let pure_prove_recursive_function_exists =
    let break_down_admissibility th1 =
      if hyp th1 = [] then th1 else
      let def = concl th1 in
      let f,bod = dest_exists def in
      let cjs = conjuncts bod in
      let eqs = map (snd o strip_forall) cjs in
      let lefts,rights = unzip(map dest_eq eqs) in
      let arglists = map (snd o strip_comb) lefts in
      let parms0 = freesl(unions arglists) in
      let parms = if parms0 <> [] then parms0 else [genvar aty] in
      let wfasm = find is_exists (hyp th1) in
      let ord,bod = dest_exists wfasm in
      let SIMP_ADMISS_TAC =
        REWRITE_TAC[LET_DEF; LET_END_DEF] THEN
        REPEAT ADMISS_TAC THEN
        TRY(W(fun (asl,w) -> let v = fst(dest_forall w) in
                X_GEN_TAC v THEN
                MAP_EVERY
                  (fun v -> TRY(GEN_REWRITE_TAC I [FORALL_PAIR_THM]) THEN
                            X_GEN_TAC v)
                  parms THEN
                CONV_TAC(TOP_DEPTH_CONV GEN_BETA_CONV) THEN
                MAP_EVERY (fun v -> SPEC_TAC(v,v)) (rev parms @ [v]))) THEN
        PURE_REWRITE_TAC[FORALL_SIMP] THEN
        W(fun (asl,w) -> MAP_EVERY (fun t -> SPEC_TAC(t,t))
                                   (subtract (frees w) [ord])) THEN
        W(fun (asl,w) -> ACCEPT_TAC(ASSUME w)) in
      let th2 = prove(bod,SIMP_ADMISS_TAC) in
      let th3 = SIMPLE_EXISTS ord th2 in
      let allasms = hyp th3 and wfasm = lhand(concl th2) in
      let th4 = ASSUME(list_mk_conj(wfasm::subtract allasms [wfasm])) in
      let th5 = SIMPLE_CHOOSE ord (itlist PROVE_HYP (CONJUNCTS th4) th3) in
      PROVE_HYP th5 th1 in
    fun dtm ->
      let th =  break_down_admissibility(instantiate_casewise_recursion dtm) in
      if concl th = dtm then th
      else failwith "prove_general_recursive_function_exists: sanity" in

(* ------------------------------------------------------------------------- *)
(* Same, but attempt to prove the wellfoundedness hyp by good guesses.       *)
(* ------------------------------------------------------------------------- *)

  let prove_general_recursive_function_exists =
    let prove_depth_measure_exists =
      let num_ty = `:num` in
      fun tyname ->
        let _,_,sth = assoc tyname (!inductive_type_store) in
        let ty,zty = dest_fun_ty
         (type_of(fst(dest_exists(snd(strip_forall(concl sth)))))) in
        let rth = INST_TYPE [num_ty,zty] sth in
        let avs,bod = strip_forall(concl rth) in
        let ev,cbod = dest_exists bod in
        let process_clause k t =
          let avs,eq = strip_forall t in
          let l,r = dest_eq eq in
          let fn,cargs = dest_comb l in
          let con,args = strip_comb cargs in
          let bargs = filter (fun t -> type_of t = ty) args in
          let r' = list_mk_binop `(+):num->num->num`
                    (mk_small_numeral k :: map (curry mk_comb fn) bargs) in
          list_mk_forall(avs,mk_eq(l,r')) in
        let cjs = conjuncts cbod in
        let def = map2 process_clause (1--length cjs) cjs in
        prove_recursive_functions_exist sth (list_mk_conj def) in
    let INDUCTIVE_MEASURE_THEN tac (asl,w) =
      let ev,bod = dest_exists w in
      let ty = fst(dest_type(fst(dest_fun_ty(type_of ev)))) in
      let th = prove_depth_measure_exists ty in
      let ev',bod' = dest_exists(concl th) in
      let th' = INST_TYPE(type_match (type_of ev') (type_of ev) []) th in
      (MP_TAC th' THEN MATCH_MP_TAC MONO_EXISTS THEN
       GEN_TAC THEN DISCH_THEN(fun th -> REWRITE_TAC[th]) THEN tac) (asl,w) in
    let CONSTANT_MEASURE_THEN =
      let one_tm = `1` in
      fun tac (asl,w) ->
        let ev,bod = dest_exists w in
        let ty = fst(dest_fun_ty(type_of ev)) in
        (EXISTS_TAC(mk_abs(genvar ty,one_tm)) THEN tac) (asl,w) in
    let GUESS_MEASURE_THEN tac =
      (EXISTS_TAC `\n. n + 1` THEN tac) ORELSE
      (INDUCTIVE_MEASURE_THEN tac) ORELSE
      CONSTANT_MEASURE_THEN tac in
    let pth_lexleft = prove
     (`(?r. WF(r) /\
            ?s. WF(s) /\
                P(\(x1,y1) (x2,y2). r x1 x2 \/ (x1 = x2) /\ s y1 y2))
       ==> ?t:A#B->A#B->bool. WF(t) /\ P t`,
      REPEAT STRIP_TAC THEN EXISTS_TAC
       `\(x1:A,y1:B) (x2:A,y2:B). r x1 x2 \/ (x1 = x2) /\ s y1 y2` THEN
      ASM_SIMP_TAC[WF_LEX]) in
    let pth_lexright = prove
     (`(?r. WF(r) /\
            ?s. WF(s) /\
                P(\(x1,y1) (x2,y2). r y1 y2 \/ (y1 = y2) /\ s x1 x2))
       ==> ?t:A#B->A#B->bool. WF(t) /\ P t`,
      REPEAT STRIP_TAC THEN
      EXISTS_TAC `\u:A#B v:A#B.
                    (\(x1:B,y1:A) (x2:B,y2:A). r x1 x2 \/ (x1 = x2) /\ s y1 y2)
                     ((\(a,b). b,a) u) ((\(a,b). b,a) v)` THEN
      ASM_SIMP_TAC[ISPEC `\(a,b). b,a` WF_MEASURE_GEN; WF_LEX; ETA_AX] THEN
      FIRST_X_ASSUM(fun th -> MP_TAC th THEN
                              MATCH_MP_TAC EQ_IMP THEN
                              AP_TERM_TAC) THEN
      REWRITE_TAC[FUN_EQ_THM; FORALL_PAIR_THM]) in
    let pth_measure = prove
     (`(?m:A->num. P(MEASURE m)) ==> ?r:A->A->bool. WF(r) /\ P r`,
      MESON_TAC[WF_MEASURE]) in
    let rec GUESS_WF_THEN tac (asl,w) =
     ((MATCH_MP_TAC pth_lexleft THEN GUESS_WF_THEN (GUESS_WF_THEN tac)) ORELSE
      (MATCH_MP_TAC pth_lexright THEN GUESS_WF_THEN (GUESS_WF_THEN tac)) ORELSE
      (MATCH_MP_TAC pth_measure THEN
       REWRITE_TAC[MEASURE; MEASURE_LE] THEN
       REWRITE_TAC[FORALL_PAIR_THM] THEN
       GUESS_MEASURE_THEN tac)) (asl,w) in
    let PRE_GUESS_TAC =
      CONV_TAC(BINDER_CONV(DEPTH_BINOP_CONV `(/\)`
       (TRY_CONV SIMPLIFY_WELLDEFINEDNESS_CONV THENC
        TRY_CONV FORALL_UNWIND_CONV))) in
    let GUESS_ORDERING_TAC =
      let false_tm = `\x:A y:A. F` in
      W(fun (asl,w) ->
            let ty = fst(dest_fun_ty(type_of(fst(dest_exists w)))) in
            EXISTS_TAC(inst [ty,aty] false_tm) THEN
            REWRITE_TAC[WF_FALSE] THEN NO_TAC) ORELSE
      GUESS_WF_THEN
       (REWRITE_TAC[FORALL_PAIR_THM] THEN ARITH_TAC) in
    fun etm ->
      let th = pure_prove_recursive_function_exists etm in
      try let wtm = find is_exists (hyp th) in
          let wth = prove(wtm,PRE_GUESS_TAC THEN GUESS_ORDERING_TAC) in
          PROVE_HYP wth th
      with Failure _ -> th in

  instantiate_casewise_recursion,
  pure_prove_recursive_function_exists,
  prove_general_recursive_function_exists;;
(* ------------------------------------------------------------------------- *) (* Simple "define" function. *) (* ------------------------------------------------------------------------- *) let define = let close_definition_clauses tm = let avs,bod = strip_forall tm in let cjs = conjuncts bod in let fs = try map (repeat rator o lhs o snd o strip_forall) cjs with Failure _ -> failwith "close_definition_clauses: non-equation" in if length (setify fs) <> 1 then failwith "close_definition_clauses: defining multiple functions" else let f = hd fs in if mem f avs then failwith "close_definition_clauses: fn quantified" else let do_clause t = let lvs,bod = strip_forall t in let fvs = subtract (frees(lhs bod)) (f::lvs) in SPECL fvs (ASSUME(list_mk_forall(fvs,t))) in let ths = map do_clause cjs in let ajs = map (hd o hyp) ths in let th = ASSUME(list_mk_conj ajs) in f,itlist GEN avs (itlist PROVE_HYP (CONJUNCTS th) (end_itlist CONJ ths)) in fun tm -> let tm' = snd(strip_forall tm) in try let th,th' = tryfind (fun th -> th,PART_MATCH I th tm') (!the_definitions) in if can (PART_MATCH I th') (concl th) then (warn true "Benign redefinition"; th') else failwith "" with Failure _ -> let f,th = close_definition_clauses tm in let etm = mk_exists(f,hd(hyp th)) in
let th1 = prove_general_recursive_function_exists etm in
      let th2 = new_specification[fst(dest_var f)] th1 in
      let g = mk_mconst(dest_var f) in
      let th3 = PROVE_HYP th2 (INST [g,f] th) in
      the_definitions := th3::(!the_definitions); th3;;