(* ========================================================================= *)
(* Miz3 interface for tactics proof, allowing readable formal proofs, *)
(* improving on miz3 mainly by allowing full use of REWRITE_TAC. *)
(* *)
(* (c) Copyright, Bill Richter 2013 *)
(* Distributed under the same license as HOL Light *)
(* *)
(* We use ideas for readable formal proofs due to John Harrison ("Towards *)
(* more readable proofs" of the tutorial and Examples/mizar.ml), Freek *)
(* Wiedijk (Mizarlight/miz2a.ml, miz3/miz3.ml and arxiv.org/pdf/1201.3601 *)
(* "A Synthesis of Procedural and Declarative Styles of Interactive *)
(* Theorem Proving"), Marco Maggesi (author of tactic constructs *)
(* INTRO_TAC, DESTRUCT_TAC & HYP), Petros Papapanagiotou (coauthor of *)
(* Isabelle Light), Vincent Aravantinos (author of the Q-module *)
(* https://github.com/aravantv/HOL-Light-Q) and Mark Adams (author of HOL *)
(* Zero and Tactician). These readability ideas yield miz3-type *)
(* "declarative" constructs like consider and case_split, and also a *)
(* miz3-type uncluttered interface, with few type annotations, backquotes or *)
(* double quotes. Many math characters can be used, as in Isabelle and HOL4: *)
(* â â ⧠⨠¬ â â â â α β γ λ θ μ â ⩠⪠â
â â¡ â
⡠⥠â â â. *)
(* The semantics of readable.ml is clear from the obvious translation to HOL *)
(* Light tactics proofs. As in HOL Light, there is an interactive mode *)
(* which is similarly useful in writing and debugging proofs. *)
(* *)
(* The construct "case_split" reducing the goal to various cases, each given *)
(* by a "suppose" clause. The construct "proof" [...] "qed" allows *)
(* arbitrarily long proofs, which can be arbitrarily nested with other *)
(* case_split and proof/qed constructs. THENL is not implemented, except *)
(* implicitly in case_split, to encourage readable proof, but it would be *)
(* easy to implement. The lack of THENL requires adjustments, such as using *)
(* MATCH_MP_TAC num_INDUCTION instead of INDUCT_TAC. *)
(* ========================================================================= *)
(* The Str library defines regexp functions needed to process strings. *)
#load "str.cma";;
(* parse_qproof uses system.ml quotexpander feature designed for miz3.ml to *)
(* turn backquoted expression `;[...]` into a string with no newline or *)
(* backslash problems. Note that miz3.ml defines parse_qproof differently. *)
let parse_qproof s = (String.sub s 1 (String.length s - 1));;
(* Allows HOL4 and Isabelle style math characters. *)
let CleanMathFontsForHOL_Light s =
let rec clean s loStringPairs =
match loStringPairs with
| [] -> s
| hd :: tl ->
let s = Str.global_replace (Str.regexp (fst hd)) (snd hd) s in
clean s tl in
clean s ["â","==>"; "â","<=>"; "â§","/\\ "; "â¨","\\/"; "¬","~";
"â","!"; "â","?"; "â","IN"; "â","NOTIN";
"α","alpha"; "β","beta"; "γ","gamma"; "λ","\\ "; "θ","theta"; "μ","mu";
"â","SUBSET"; "â©","INTER"; "âª","UNION"; "â
","{}"; "â","DELETE";
"â¡","==="; "â
","cong"; "â¡","angle"; "â¥","parallel";
"â","prod"; "â","_o_";"â","--->"];;
(* printReadExn prints uncluttered error messages via Readable_fail. This *) (* is due to Mark Adams, who also explained Roland Zumkeller's exec below. *)
exception Readable_fail of string;;
let printReadExn e =
match e with
| Readable_fail s
-> print_string s
| _ -> print_string (Printexc.to_string e);;
#install_printer printReadExn;;
(* From update_database.ml: Execute any OCaml expression given as a string. *)
let exec = ignore o Toploop.execute_phrase false Format.std_formatter
o !Toploop.parse_toplevel_phrase o Lexing.from_string;;
(* exec and a reference idea from miz3.ml yield is_thm, a predicate asking *)
(* if a string represents a theorem, and exec_thm, which returns the thm. *)
let thm_ref = ref TRUTH;;
let tactic_ref = ref ALL_TAC;;
let thmlist_tactic_ref = ref REWRITE_TAC;;
let thmtactic_ref = ref MATCH_MP_TAC;;
let is_thm s =
try exec ("thm_ref := ((" ^ s ^ "): thm);;"); true
with _ -> false;;
let exec_thm s =
try if (is_thm s) then !thm_ref else raise Noparse
with _ -> raise Noparse;;
let exec_tactic s =
try exec ("tactic_ref := ((" ^ s ^ "): tactic);;"); !tactic_ref
with _ -> raise Noparse;;
let exec_thmlist_tactic s =
try
exec ("thmlist_tactic_ref := ((" ^ s ^ "): thm list -> tactic);;");
!thmlist_tactic_ref
with _ -> raise Noparse;;
let exec_thmtactic s =
try exec ("thmtactic_ref := ((" ^ s ^ "): thm -> tactic);;"); !thmtactic_ref
with _ -> raise Noparse;;
(* make_env and parse_env_string, following Mizarlight/miz2a.ml and Vince *)
(* Aravantinos's https://github.com/aravantv/HOL-Light-Q, turn a string to a *)
(* term with types inferred by the goal and assumption list. *)
let (make_env: goal -> (string * pretype) list) =
fun (asl, w) -> map ((fun (s, ty) -> (s, pretype_of_type ty)) o dest_var)
(freesl (w::(map (concl o snd) asl)));;
let parse_env_string env s = (term_of_preterm o retypecheck env)
((fst o parse_preterm o lex o explode) s);;
(* versions of new_constant, parse_as_infix, new_definition and new_axiom *)
let NewConstant (x, y) = new_constant(CleanMathFontsForHOL_Light x, y);;
let ParseAsInfix (x, y) = parse_as_infix (CleanMathFontsForHOL_Light x, y);;
let NewDefinition s =
new_definition (parse_env_string [] (CleanMathFontsForHOL_Light s));;
let NewAxiom s =
new_axiom (parse_env_string [] (CleanMathFontsForHOL_Light s));;
(* String versions without type annotations of SUBGOAL_THEN, SUBGOAL_TAC, *)
(* EXISTS_TAC, X_GEN_TAC, EXISTS_TAC and MP_TAC o SPECL. *)
let subgoal_THEN stm ttac gl =
SUBGOAL_THEN (parse_env_string (make_env gl) stm) ttac gl;;
let subgoal_TAC s stm prf gl =
SUBGOAL_TAC s (parse_env_string (make_env gl) stm) [prf] gl;;
let exists_TAC stm gl =
EXISTS_TAC (parse_env_string (make_env gl) stm) gl;;
let X_gen_TAC svar (asl, w as gl) =
let vartype = (snd o dest_var o fst o dest_forall) w in
X_GEN_TAC (mk_var (svar, vartype)) gl;;
let mp_TAC_specl stermlist sthm gl =
try
let termlist = map (fun s -> parse_env_string (make_env gl) s) stermlist in
(MP_TAC o ISPECL termlist) (exec_thm sthm) gl
with _ -> raise (Readable_fail ("This is not an mp_TAC_specl expression:
mp_TAC_specl [" ^ (String.concat "; " stermlist) ^ "] "^ sthm));;
(* assume transforms a disjunct goal α ⨠β into an implication ¬α â β and *)
(* discharges ¬α. raa allows proofs by contradiction (reductio ad absurdum). *)
let assume lab notalpha tac (asl, w as gl) =
let t = parse_env_string (make_env gl) notalpha in
let notalpha_implies_beta = mk_imp(t, mk_conj(t, w)) in
(SUBGOAL_THEN notalpha_implies_beta (LABEL_TAC lab) THENL
[INTRO_TAC lab; tac] THEN
HYP REWRITE_TAC lab [MESON[] `!x y. ~x ==> (~x /\ (x \/ y) <=> y)`]) gl;;
let raa lab st tac = subgoal_THEN (st ^ " ==> F") (LABEL_TAC lab) THENL
[INTRO_TAC lab; tac];;
let case_split sDestruct sDisjlist tac =
let rec list_mk_string_disj = function
[] -> ""
| s::[] -> "(" ^ s ^ ")"
| s::ls -> "(" ^ s ^ ") \\/ " ^ list_mk_string_disj ls in
subgoal_TAC "" (list_mk_string_disj sDisjlist) tac THEN
FIRST_X_ASSUM (DESTRUCT_TAC sDestruct);;
(* Basically from the HOL Light tutorial "Towards more readable proofs." *)
let arithmetic = ARITH_TAC;;
let set_RULE = CONV_TAC SET_RULE;;
let real_RING = CONV_TAC REAL_RING;;
let fol = MESON_TAC;;
let TACtoThmTactic tac = fun ths -> MAP_EVERY MP_TAC ths THEN tac;;
let NUM_RING_thmTAC = TACtoThmTactic (CONV_TAC NUM_RING);;
let ARITH_thmTAC = TACtoThmTactic ARITH_TAC;;
let REAL_ARITH_thmTAC = TACtoThmTactic REAL_ARITH_TAC;;
let set = TACtoThmTactic set_RULE;;
let so = fun tac -> FIRST_ASSUM MP_TAC THEN tac;;
let ws = "[ \t\n]+";;
let ws0 = "[ \t\n]*";;
let StringRegexpEqual r s = Str.string_match r s 0 &&
s = Str.matched_string s;;
(* FindMatch sleft sright s *)
(* turns strings sleft and sright into regexps, recursively searches string *)
(* s for matched pairs of substrings matching sleft and sright, and returns *)
(* the position after the first substring matched by sright which is not *)
(* paired with an sleft-matching substring. Often here sleft ends with *)
(* whitespace (ws) while sright begins with ws. The "degenerate" case of *)
(* X^ws^Y where X^ws matches sleft and ws^Y matches sright is handled by *)
(* backing up a character after an sleft match if the last character is ws. *)
let FindMatch sleft sright s =
let test = Str.regexp ("\("^ sleft ^"\|"^ sright ^"\)")
and left = Str.regexp sleft in
let rec FindMatchPosition s count =
if count = 1 then 0
else
try
ignore(Str.search_forward test s 0);
let TestMatch = Str.matched_group 1 s
and AfterTest = Str.match_end() in
let LastChar = Str.last_chars (Str.string_before s AfterTest) 1 in
let endpos =
if Str.string_match (Str.regexp ws) LastChar 0
then AfterTest - 1 else AfterTest in
let rest = Str.string_after s endpos
and increment =
if StringRegexpEqual left TestMatch then -1 else 1 in
endpos + (FindMatchPosition rest (count + increment))
with Not_found -> raise (Readable_fail
("No matching right bracket operator "^ sright ^
" to left bracket operator "^ sleft ^" in "^ s)) in
FindMatchPosition s 0;;
(* FindSemicolon uses FindMatch to find the position before the next *)
(* semicolon which is not a delimiter of a list. *)
let rec FindSemicolon s =
try
let rec FindMatchPosition s pos =
let start = Str.search_forward (Str.regexp ";\|\[") s pos in
if Str.matched_string s = ";" then start
else
let rest = Str.string_after s (start + 1) in
let MatchingSquareBrace = FindMatch "\[" "\]" rest in
let newpos = start + 1 + MatchingSquareBrace in
FindMatchPosition s newpos in
FindMatchPosition s 0
with Not_found -> raise (Readable_fail ("No final semicolon in " ^ s));;
(* GetProof uses FindMatch to find substrings of the sort *)
(* "proof" body "qed;" *)
(* in a substring s that begins after the "proof", skipping over *)
(* "proof" ... "qed;" substrings that occur in body. *)
let GetProof ByProof s =
if ByProof = "by" then
let pos = FindSemicolon s in
let step, rest = Str.string_before s pos, Str.string_after s (Str.match_end()) in
(step ^ " ;", rest)
else
let pos_after_qed = FindMatch (ws^"proof"^ws) (ws^"qed"^ws0^";") s in
let pos = Str.search_backward (Str.regexp "qed") s pos_after_qed in
(Str.string_before s pos, Str.string_after s pos_after_qed);;
(* FindCases uses FindMatch to take a string *)
(* "suppose" proof_1 "end;" ... "suppose" proof_n "end;" *)
(* and return the list [proof_1; proof_2; ... ; proof_n]. *)
let rec FindCases s =
let sleftCase, srightCase = ws^ "suppose" ^ws, ws^ "end" ^ws0^ ";" in
if Str.string_match (Str.regexp sleftCase) s 0 then
let CaseEndRest = Str.string_after s (Str.match_end()) in
let PosAfterEnd = FindMatch sleftCase srightCase CaseEndRest in
let pos = Str.search_backward (Str.regexp srightCase) CaseEndRest PosAfterEnd in
let case = Str.string_before CaseEndRest pos
and rest = Str.string_after CaseEndRest PosAfterEnd in
case :: (FindCases rest)
else [];;
(* StringToList uses FindSemicolon to turns a string into the list of *)
(* substrings delimited by the semicolons which are not captured in lists. *)
let rec StringToList s =
if Str.string_match (Str.regexp "[^;]*;") s 0 then
let pos = FindSemicolon s in
let head = Str.string_before s pos in
head :: (StringToList (Str.string_after s (pos + 1)))
else [s];;
(* StringToTactic uses regexp functions from the Str library to transform a *)
(* string into a tactic. The allowable tactics can be written in BNF form *)
(* as *)
(* Tactic := ALL_TAC | Tactic THEN Tactic | *)
(* one-word-tactic (e.g. ARITH_TAC) | *)
(* one-word-thm_tactic one-word-thm (e.g. MATCH_MP_TAC num_WF) | *)
(* one-word-thmlist_tactic listof(thm | label | - | --) | *)
(* intro_TAC string | exists_TAC string | X_gen_TAC term | *)
(* mp_TAC_specl listof(term) theorem | *)
(* case_split string listof(statement) Tactic THENL listof(Tactic) | *)
(* consider listof(variable) such that statement [label] Tactic | *)
(* raa label statement Tactic | assume label statement Tactic | *)
(* subgoal_TAC label statement Tactic *)
(* *)
(* The allowable string proofs which StringToTactic transforms into tactics *)
(* can be written in BNF form as *)
(* *)
(* OneStepProof := one-word-tactic ";" (e.g. "ARITH_TAC;") | *)
(* one-word-thm_tactic one-word-thm ";" (e.g. "MATCH_MP_TAC num_WF;") | *)
(* one-word-thmlist_tactic concatenationof(thm | label | - | --) ";" | *)
(* "intro_TAC" string ";" | "exists_TAC" term ";" | "X_gen_TAC" var ";" *)
(* "mp_TAC_specl" listof(term) one-word-thm";" *)
(* *)
(* ByProofQed := "by" OneStepProof | "proof" Proof Proof ... Proof "qed;" *)
(* *)
(* Proof := "" | OneStepProof | Proof Proof | *)
(* "consider" variable-list "such that" term [label] ByProofQed | *)
(* "raa" statement [label] ByProofQed | *)
(* "assume" statement [label] ByProofQed | statement [label] ByProofQed | *)
(* "case_split" destruct ByProofQed *)
(* "suppose" statement ";" Proof "end;" ... *)
(* "suppose" statement ";" Proof "end;" *)
(* *)
(* Case_split reduces the goal to various cases. In the case_split *)
(* above, ByProofQed is a proof of the disjunction of the statements, and *)
(* each Proof solves the goal with its statement added as an assumption. *)
(* The string destruct lab1 | lab2 | ... has the syntax of DESTRUCT_TAC, *)
(* so lab1 is the label of the first statement in the first case, etc. *)
(* The unidentified lower-case words above, e.g. term and statement, are *)
(* strings. The label of consider and intro_TAC must also be be nonempty. *)
(* raa x [l] ByProofQed; *)
(* is a proof by contradiction (reductio ad absurdum). ByProofQed proves *)
(* the goal using the added assumption ~x. There is a new subgoal F (false) *)
(* which has the added assumption x, also labeled l, which must be nonempty. *)
(* If - is used by ByProofQed, it will refer to ~x, also labeled l. *)
(* assume x [l] ByProofQed; *)
(* turns a disjunction goal into a implication with discharged antecedent. *)
(* ByProofQed is a proof that the goal is equivalent to a disjunction *)
(* x \/ y, for some statement y. The label l must nonempty. Then the goal *)
(* becomes y, with x an assumption labeled l. *)
(* statement [label] ByProofQed; *)
(* is the workhorse of declarative proofs, based on subgoal_TAC: a statement *)
(* with a label and a proof ByProofQed. Use [] if statement is never used *)
(* except in the next line, where it can be referred to as - or --. *)
(* thmlist->tactic ListofLabel-Theorem--; *)
(* is a tactic constructed by a thmlist->tactic, e.g. MESON_TAC (written as *)
(* fol) followed by a space-separated list of labels, theorems and - or --, *)
(* which both refer to the previous statement, constructed by HYP. If -- *)
(* occurs, the previous statement is used by so (using FIRST_ASSUM). If - *)
(* occurs, the previous statement is used the way that HYP uses theorems. *)
(* *)
(* Detected errors which result in a failure and an error message: *)
(* 1) Square braces [...] must be matched. *)
(* 2) "proof" must be matched by "qed;", or more precisely, *)
(* ws^ "proof" ^ws must be matched by ";" ^ws^ "qed;", *)
(* where ws means nonempty whitespace, except in the skeleton proof *)
(* "proof" ws "qed;" *)
(* 3) In a case_split environment, *)
(* ws^ "suppose" ^ws must be matched by ws^ "end;". *)
(* 4) Each step in a proof must end with ";". *)
(* 5) A proof must match the BNF for Proof. *)
(* 6) mp_TAC_specl expression must work; e.g. the theorem must be a theorem. *)
let rec StringToTactic s =
if StringRegexpEqual (Str.regexp ws0) s then ALL_TAC
else if Str.string_match (Str.regexp (ws^ "case_split" ^ws^ "\([^;]+\)" ^ws^
"\(by\|proof\)" ^ws)) s 0 then
let sDestruct = Str.matched_group 1 s
and (proof, rest) = GetProof (Str.matched_group 2 s)
(Str.string_after s (Str.group_end 2))
and SplitAtSemicolon case = Str.bounded_split (Str.regexp ";") case 2 in
let list2Case = map SplitAtSemicolon (FindCases rest) in
let listofDisj, listofTac = map hd list2Case, map (hd o tl) list2Case in
(case_split sDestruct listofDisj (StringToTactic proof))
THENL (map StringToTactic listofTac)
else
let pos = FindSemicolon s in
let step, rest = Str.string_before s pos, Str.string_after s (Str.match_end()) in
let (tactic, rest) = StepToTactic s step rest in
tactic THEN StringToTactic rest
and
StepToTactic s step rest =
try
if StringRegexpEqual (Str.regexp (ws0^ "\([^ \t\n]+\)" ^ws0)) step then
(exec_tactic (Str.matched_group 1 step), rest)
else raise Not_found
with _ ->
try
if StringRegexpEqual (Str.regexp (ws0^ "\([^][ \t\n]+\)" ^ws0^
"\([^][ \t\n]+\)" ^ws0)) step then
((exec_thmtactic (Str.matched_group 1 step))
(exec_thm (Str.matched_group 2 step)), rest)
else raise Not_found
with _ ->
try
if StringRegexpEqual (Str.regexp (ws0^ "\([^ \t\n]+\)\(" ^ws0^
"[^[]*" ^ws0^ "\)" ^ws0)) step then
let ttac = exec_thmlist_tactic (Str.matched_group 1 step)
and LabThmList = Str.split (Str.regexp ws) (Str.matched_group 2 step) in
let thms = filter is_thm LabThmList
and labs0 = String.concat " " (filter (not o is_thm) LabThmList) in
let labs, listofThms = " "^ labs0 ^" ", map exec_thm thms in
if Str.string_match (Str.regexp ("[^-]*" ^ws^ "-" ^ws)) labs 0 then
let labs = Str.global_replace (Str.regexp (ws^ "-")) "" labs in
let tactic = fun (asl, w as gl) ->
(HYP ttac labs ((snd (hd asl)) :: listofThms)) gl in
(tactic, rest)
else if Str.string_match (Str.regexp ("[^-]*" ^ws^ "--" ^ws)) labs 0 then
let labs = Str.global_replace (Str.regexp (ws^ "--")) "" labs in
(so (HYP ttac labs listofThms), rest)
else (HYP ttac labs listofThms, rest)
else raise Not_found
with _ ->
if Str.string_match (Str.regexp (ws0^ "intro_TAC" ^ws)) step 0 then
let intro_string = (Str.global_replace (Str.regexp ",") ";"
(Str.string_after step (Str.match_end()))) in
(INTRO_TAC intro_string, rest)
else if Str.string_match (Str.regexp (ws0^ "exists_TAC" ^ws)) step 0 then
let exists_string = Str.string_after step (Str.match_end()) in
(exists_TAC exists_string, rest)
else if Str.string_match (Str.regexp (ws0^ "X_gen_TAC" ^ws)) step 0 then
let gen_string = Str.string_after step (Str.match_end()) in
(X_gen_TAC gen_string, rest)
else if
Str.string_match (Str.regexp (ws0^ "mp_TAC_specl" ^ws^ "\[")) step 0 then
let ListWsThm = Str.string_after step (Str.match_end()) in
let RightBrace = FindMatch "\[" "\]" ListWsThm in
let WsThm = Str.string_after ListWsThm RightBrace
and tlist = StringToList (Str.string_before ListWsThm (RightBrace - 1)) in
if StringRegexpEqual (Str.regexp (ws^ "\([^ \t\n]+\)")) WsThm then
(mp_TAC_specl tlist (Str.matched_group 1 WsThm), rest)
else raise (Readable_fail (step ^ " is not an mp_TAC_specl expression"))
else BigStepToTactic s step
and
BigStepToTactic s step =
if Str.string_match (Str.regexp (ws0^ "consider" ^ws^ "\(\(.\|\n\)+\)" ^ws^
"such" ^ws^ "that" ^ws^ "\(\(.\|\n\)+\)" ^ws^ "\[\(\(.\|\n\)*\)\]" ^ws^
"\(by\|proof\)" ^ws)) step 0 then
let vars, t = Str.matched_group 1 step, Str.matched_group 3 step
and lab, KeyWord = Str.matched_group 5 step, Str.matched_group 7 step in
let (proof, rest) = GetProof KeyWord (Str.string_after s (Str.group_end 7))
and tactic = subgoal_THEN ("?" ^ vars ^ ". " ^ t)
(DESTRUCT_TAC ("@" ^ vars ^ "." ^ lab)) in
(tactic THENL [StringToTactic proof; ALL_TAC], rest)
else
try
let start = Str.search_forward (Str.regexp
(ws^ "\[\([^]]*\)\]" ^ws^ "\(by\|proof\)" ^ws)) step 0 in
let statement, lab = Str.string_before step start, Str.matched_group 1 step
and KeyWord = Str.matched_group 2 step
and AfterWord = Str.string_after s (Str.group_end 2) in
let (proof, rest) = GetProof KeyWord AfterWord in
if Str.string_match (Str.regexp (ws0^ "\(raa\|assume\)" ^ws)) statement 0
then
let statement = Str.string_after statement (Str.match_end()) in
if Str.matched_group 1 step = "raa" then
(raa lab statement (StringToTactic proof), rest)
else (assume lab statement (StringToTactic proof), rest)
else (subgoal_TAC lab statement (StringToTactic proof), rest)
with Not_found -> raise (Readable_fail ("can't parse "^ step));;
let theorem s =
let s = CleanMathFontsForHOL_Light s in
try
let start = Str.search_forward (Str.regexp
(ws ^ "proof" ^ws^ "\(\(.\|\n\)*\)" ^ws ^ "qed" ^ws0^ ";" ^ws0)) s 0 in
let thm, proof = Str.string_before s start, Str.matched_group 1 s in
prove (parse_env_string [] thm, StringToTactic proof)
with Not_found -> raise (Readable_fail
("Missing initial \"proof\" or final \"qed;\" in\n" ^ s));;
let interactive_proof s =
let proof = CleanMathFontsForHOL_Light s in
e (StringToTactic proof);;
let interactive_goal s =
let thm = CleanMathFontsForHOL_Light s in
g (parse_env_string [] thm);;
(* The uncluttered interface is shown by a proof from the HOL Light tutorial *)
(* section 14.1 "Choice and the select operator" and a proof from arith.ml. *)
let AXIOM_OF_CHOICE = theorem `;
âP. (âx. ây. P x y) â âf. âx. P x (f x)
proof
intro_TAC âP, H1;
exists_TAC λx. @y. P x y;
fol H1;
qed;
`;;
let MOD_MOD_REFL = theorem `;
âm n. ¬(n = 0) â ((m MOD n) MOD n = m MOD n)
proof
intro_TAC !m n, H1;
mp_TAC_specl [m; n; 1] MOD_MOD;
fol H1 MULT_CLAUSES MULT_EQ_0 ONE NOT_SUC;
qed;
`;;
let NSQRT_2 = theorem `;
âp q. p * p = 2 * q * q â q = 0
proof
MATCH_MP_TAC num_WF;
intro_TAC âp, A, âq, B;
EVEN(p * p) â EVEN(2 * q * q) [] by fol B;
EVEN(p) [] by fol - ARITH EVEN_MULT;
consider m such that p = 2 * m [C] by fol - EVEN_EXISTS;
case_split qp | pq by arithmetic;
suppose q < p;
q * q = 2 * m * m â m = 0 [] by fol qp A;
NUM_RING_thmTAC - B C;
end;
suppose p <= q;
p * p <= q * q [] by fol - LE_MULT2;
q * q = 0 [] by ARITH_thmTAC - B;
NUM_RING_thmTAC -;
end;
qed;
`;;
(* The following interactive version of the above proof shows a feature of *)
(* proof/qed and case_split/suppose. You can evaluate an incomplete proof *)
(* of a statement in an interactive_proof and complete the proof afterward, *)
(* as indicated below. The "suppose" clauses of a case_split can also be *)
(* incomplete. Do not include code below the incomplete proof or case_split *)
(* in an interactive_proof body, for the usual THEN vs THENL reason. *)
interactive_goal `;âp q. p * p = 2 * q * q â q = 0
`;;
interactive_proof `;
MATCH_MP_TAC num_WF;
intro_TAC âp, A, âq, B;
EVEN(p * p) â EVEN(2 * q * q) [] proof qed;
`;;
interactive_proof `;
fol B;
`;;
interactive_proof `;
EVEN(p) [] by fol - ARITH EVEN_MULT;
consider m such that p = 2 * m [C] proof fol - EVEN_EXISTS; qed;
`;;
interactive_proof `;
case_split qp | pq by arithmetic;
suppose q < p;
end;
suppose p <= q;
end;
`;;
interactive_proof `;
q * q = 2 * m * m â m = 0 [] by fol qp A;
NUM_RING_thmTAC - B C;
`;;
interactive_proof `;
p * p <= q * q [] by fol - LE_MULT2;
q * q = 0 [] by ARITH_thmTAC - B;
NUM_RING_thmTAC -;
`;;
let NSQRT_2 = top_thm();;