2 Author: Thomas C. Hales, 2003
4 GCD_CONV takes two HOL-light terms (NUMERALs) a and b and
5 produces a theorem of the form
8 (In particular, the arguments cannot be negative.)
15 let DIVIDE = new_definition(`DIVIDE a b = ?m. (b = m*a )`);;
17 parse_as_infix("||",(16,"right"));;
19 override_interface("||",`DIVIDE:num->num->bool`);;
21 (* Now prove the lemmas *)
23 let DIV_TAC t = EVERY[ REP_GEN_TAC;
26 REPEAT (FIRST_X_ASSUM CHOOSE_TAC);
30 let DIVIDE_DIVIDE = prove_by_refinement(
31 `!a b c. (((a || b) /\ (b || c)) ==> (a || c))`,
34 ASM_REWRITE_TAC[MULT_ASSOC]
37 let DIVIDE_EQ = prove_by_refinement(
38 `! a b. (((a || b) /\ (b || a)) ==> (a = b))`,
41 FIRST_X_ASSUM (fun th -> (POP_ASSUM MP_TAC) THEN REWRITE_TAC[th]);
45 REWRITE_TAC[ARITH_RULE `(b = m*m'*b) = (1*b = m*m'*b)`];
46 ASM_REWRITE_TAC[MULT_ASSOC;EQ_MULT_RCANCEL];
47 DISCH_THEN (fun th -> MP_TAC (REWRITE_RULE[MULT_EQ_1] (GSYM th)) );
48 DISCH_THEN (fun th -> REWRITE_TAC[CONJUNCT2 th] THEN ARITH_TAC);
51 let DIVIDE_SUM = prove_by_refinement(
52 `!a b h. (((h || a) /\ (h||b)) ==> (h || (a+b)))`,
55 ASM_REWRITE_TAC[ARITH;RIGHT_ADD_DISTRIB];
58 let DIVIDE_SUMMAND = prove_by_refinement(
59 `!a b h. (((h|| b) /\ (h || (a+b))) ==> (h|| a))`,
62 REWRITE_TAC[RIGHT_SUB_DISTRIB];
63 REPEAT (FIRST_X_ASSUM (fun th -> REWRITE_TAC[GSYM th]));
67 let DIVIDE_PROD = prove_by_refinement(
68 `!a b h. (((h|| a) ==> (h || (b*a))))`,
71 ASM_REWRITE_TAC[MULT_ASSOC];
74 let DIVIDE_PROD2 = prove_by_refinement(
75 `!a b h. (((h|| a) ==> (h || (a*b))))`,
78 ASM_REWRITE_TAC[MULT_AC]
81 let GCD = new_definition(`GCD a b = @g.
82 ((g || a) /\ (g || b) /\
83 (!h. (((h || a) /\ (h || b)) ==> (h || g))))`);;
85 let gcd_certificate = prove(`!a b g. ((? r s r' s' a' b'.
86 ((a = a'*g) /\ (b = b'*g) /\ (g +r'*a+s'*b= r*a + s*b)))
91 THEN (REPEAT (POP_ASSUM CHOOSE_TAC))
92 THEN (REWRITE_TAC[GCD])
93 THEN (MATCH_MP_TAC SELECT_UNIQUE)
100 THEN (MATCH_MP_TAC DIVIDE_EQ)
104 (SUBGOAL_TAC (` (y || (r*a + s*b))/\ (y || (r'*a +s'*b))`))
105 THENL [((ASM MESON_TAC)[DIVIDE_SUM;DIVIDE_PROD]);
106 ((ASM MESON_TAC)[DIVIDE_SUMMAND])]
111 (`(y||a) /\ (y ||b) /\ (!h. (((h||a)/\(h||b))==> (h||y)))`))
112 THEN (TAUT_TAC (` (A ==> B) ==> ((C /\ D/\ A)==> B)`))
114 THEN (POP_ASSUM MATCH_MP_TAC)
115 THEN (REWRITE_TAC[DIVIDE])
117 THEN ((ASM MESON_TAC)[])
122 THEN (let x t = REWRITE_TAC[t] in (POP_ASSUM x))
124 THENL [((ASM MESON_TAC)[DIVIDE]);ALL_TAC]
126 THENL [((ASM MESON_TAC)[DIVIDE]);ALL_TAC]
129 THEN (SUBGOAL_TAC (` (h || (r*a + s*b))/\ (h || (r'*a+s'*b))`))
130 THENL [((ASM MESON_TAC)[DIVIDE_SUM;DIVIDE_PROD]);
131 ((ASM MESON_TAC)[DIVIDE_SUMMAND])]
133 tac1 THENL [ygbranch THENL [ydivg_branch;gdivy_branch];yghyp_branch]);;
135 (* Now compute gcd with CAML num calculations,
136 then check the answer in HOL-light *)
138 let rec gcd_data (a1,b1,x1,a2,b2,x2) =
139 if (x1 < (Int 0)) then
140 gcd_data(minus_num a1,minus_num b1,minus_num x1,a2,b2,x2)
141 else if (x2 < (Int 0)) then gcd_data(a1,b1,x1,minus_num a2,minus_num
143 else if (x1 = (Int 0)) then (a2,b2,x2)
144 else if (x1>x2) then gcd_data (a2,b2,x2,a1,b1,x1)
146 let r = (quo_num x2 x1) in
147 gcd_data (a1,b1,x1,a2 -/ r*/ a1,b2 -/ r*/ b1, x2 -/ r*/ x1)
149 gcd_data ((Int 1),(Int 0),x1,(Int 0),(Int 1),x2);;
152 let rec gcd_data (a1,b1,x1,a2,b2,x2) =
153 if (x1 < (Int 0)) then
154 gcd_data(minus_num a1,minus_num b1,minus_num x1,a2,b2,x2)
155 else if (x2 < (Int 0)) then gcd_data(a1,b1,x1,minus_num a2,minus_num
157 else if (x1 = (Int 0)) then (a2,b2,x2)
158 else if (x1>x2) then gcd_data (a2,b2,x2,a1,b1,x1)
160 let r = (quo_num x2 x1) in
161 gcd_data (a1,b1,x1,a2 -/ r*/ a1,b2 -/ r*/ b1, x2 -/ r*/ x1)
163 gcd_data ((Int 1),(Int 0),x1,(Int 0),(Int 1),x2);;
165 (* g = gcd, (a',b') = (a,b)/g, g +r1'*a+s1'*b = r1*a+s1*b *)
166 let gcd_numdata a b =
170 let (r,s,g) = gcd_num a b in
171 let a' = if (g=Z) then Z else round_num(a//g) in
172 let b' = if (g=Z) then Z else round_num(b//g) in
173 let _ = if not(a=a'*/g) then failwith "GCD_CONV a" else 0 in
174 let _ = if not(b=b'*/g) then failwith "GCD_CONV b" else 0 in
175 let _ = if not(g=r*/a+/s*/b) then failwith "GCD_CONV g" else 0 in
176 let (r1,r1') = if (r >/ Z) then (r,Z) else (Z,minus_num r) in
177 let (s1,s1') = if (s >/ Z) then (s,Z) else (Z,minus_num s) in
178 (g,a,b,a',b',r1',s1',r1,s1);;
180 (* Here is the conversion.
182 GCD_CONV (`66`) (`144`)
186 let a = dest_numeral at in
187 let b = dest_numeral bt in
188 let (g,a,b,a',b',r1',s1',r1,s1) = gcd_numdata a b in
189 prove(parse_term("GCD "^(string_of_num a)^" "^(string_of_num b)^" = "^
191 (MATCH_MP_TAC gcd_certificate)
192 THEN (EXISTS_TAC (mk_numeral r1))
193 THEN (EXISTS_TAC (mk_numeral s1))
194 THEN (EXISTS_TAC (mk_numeral r1'))
195 THEN (EXISTS_TAC (mk_numeral s1'))
196 THEN (EXISTS_TAC (mk_numeral a'))
197 THEN (EXISTS_TAC (mk_numeral b'))
203 This version can overflow on CAML integers before it reaches hol-light.
205 hol_gcd 1000000000000000000 10000000000000000000000
206 - : thm = |- GCD 660865024 843055104 = 262144
209 let hol_gcd a b = GCD_CONV (mk_small_numeral a) (mk_small_numeral b);;
211 remove_interface ("||");;
217 exception Test_suite_num_ext_gcd of string;;
219 (* For the tests we use integers a and b. These can overflow if
220 a and b are too large, so that we should confine ourselves to
221 tests that are not too large.
224 let test_num_ext_gcd (a, b) =
225 let a1 = string_of_int (abs a) in
226 let b1 = string_of_int (abs b) in
228 let c1 = string_of_int (abs c) in
229 let th = GCD_CONV (mk_small_numeral a) (mk_small_numeral b) in
230 if (not (hyp th = ([]:term list))) then raise
231 (failwith ("num_ext_gcd test suite failure "^a1^" "^b1))
232 else if (not (concl th = (parse_term ("GCD "^a1^" "^b1^"="^c1))))
233 then raise (failwith ("num_ext_gcd test suite failure "^a1^" "^b1))
237 let test_suite_num_ext_gcd =
240 [(0,0);(0,1);(1,0);(-0,-0);
243 (10,100);(100,10);(17,100);(100,17)] in
244 print_string "num_ext_gcd loaded\n";;
246 let divide = DIVIDE and
248 gcd_conv = GCD_CONV;;