1 (* Author: Lukas Bulwahn, TU Muenchen *)
3 header {* Counterexample generator performing narrowing-based testing *}
5 theory Quickcheck_Narrowing
6 imports Quickcheck_Exhaustive
8 ("Tools/Quickcheck/PNF_Narrowing_Engine.hs")
9 ("Tools/Quickcheck/Narrowing_Engine.hs")
10 ("Tools/Quickcheck/narrowing_generators.ML")
13 subsection {* Counterexample generator *}
15 text {* We create a new target for the necessary code generation setup. *}
17 setup {* Code_Target.extend_target ("Haskell_Quickcheck", (Code_Haskell.target, K I)) *}
19 subsubsection {* Code generation setup *}
22 (Haskell_Quickcheck "Typerep")
24 code_const Typerep.Typerep
25 (Haskell_Quickcheck "Typerep")
27 code_reserved Haskell_Quickcheck Typerep
29 subsubsection {* Type @{text "code_int"} for Haskell Quickcheck's Int type *}
31 typedef (open) code_int = "UNIV \<Colon> int set"
32 morphisms int_of of_int by rule
34 lemma of_int_int_of [simp]:
35 "of_int (int_of k) = k"
36 by (rule int_of_inverse)
38 lemma int_of_of_int [simp]:
39 "int_of (of_int n) = n"
40 by (rule of_int_inverse) (rule UNIV_I)
43 "(\<And>n\<Colon>code_int. PROP P n) \<equiv> (\<And>n\<Colon>int. PROP P (of_int n))"
46 assume "\<And>n\<Colon>code_int. PROP P n"
47 then show "PROP P (of_int n)" .
50 assume "\<And>n\<Colon>int. PROP P (of_int n)"
51 then have "PROP P (of_int (int_of n))" .
52 then show "PROP P n" by simp
56 lemma int_of_inject [simp]:
57 "int_of k = int_of l \<longleftrightarrow> k = l"
58 by (rule int_of_inject)
60 lemma of_int_inject [simp]:
61 "of_int n = of_int m \<longleftrightarrow> n = m"
62 by (rule of_int_inject) (rule UNIV_I)+
64 instantiation code_int :: equal
68 "HOL.equal k l \<longleftrightarrow> HOL.equal (int_of k) (int_of l)"
71 qed (auto simp add: equal_code_int_def equal_int_def eq_int_refl)
75 instantiation code_int :: number
85 lemma int_of_number [simp]:
86 "int_of (number_of k) = number_of k"
87 by (simp add: number_of_code_int_def number_of_is_id)
90 definition nat_of :: "code_int => nat"
92 "nat_of i = nat (int_of i)"
95 code_datatype "number_of \<Colon> int \<Rightarrow> code_int"
98 instantiation code_int :: "{minus, linordered_semidom, semiring_div, linorder}"
101 definition [simp, code del]:
104 definition [simp, code del]:
107 definition [simp, code del]:
108 "n + m = of_int (int_of n + int_of m)"
110 definition [simp, code del]:
111 "n - m = of_int (int_of n - int_of m)"
113 definition [simp, code del]:
114 "n * m = of_int (int_of n * int_of m)"
116 definition [simp, code del]:
117 "n div m = of_int (int_of n div int_of m)"
119 definition [simp, code del]:
120 "n mod m = of_int (int_of n mod int_of m)"
122 definition [simp, code del]:
123 "n \<le> m \<longleftrightarrow> int_of n \<le> int_of m"
125 definition [simp, code del]:
126 "n < m \<longleftrightarrow> int_of n < int_of m"
130 qed (auto simp add: code_int left_distrib zmult_zless_mono2)
134 lemma zero_code_int_code [code, code_unfold]:
135 "(0\<Colon>code_int) = Numeral0"
136 by (simp add: number_of_code_int_def Pls_def)
137 lemma [code_post]: "Numeral0 = (0\<Colon>code_int)"
138 using zero_code_int_code ..
140 lemma one_code_int_code [code, code_unfold]:
141 "(1\<Colon>code_int) = Numeral1"
142 by (simp add: number_of_code_int_def Pls_def Bit1_def)
143 lemma [code_post]: "Numeral1 = (1\<Colon>code_int)"
144 using one_code_int_code ..
147 definition div_mod_code_int :: "code_int \<Rightarrow> code_int \<Rightarrow> code_int \<times> code_int" where
148 [code del]: "div_mod_code_int n m = (n div m, n mod m)"
151 "div_mod_code_int n m = (if m = 0 then (0, n) else (n div m, n mod m))"
152 unfolding div_mod_code_int_def by auto
155 "n div m = fst (div_mod_code_int n m)"
156 unfolding div_mod_code_int_def by simp
159 "n mod m = snd (div_mod_code_int n m)"
160 unfolding div_mod_code_int_def by simp
162 lemma int_of_code [code]:
163 "int_of k = (if k = 0 then 0
164 else (if k mod 2 = 0 then 2 * int_of (k div 2) else 2 * int_of (k div 2) + 1))"
166 have 1: "(int_of k div 2) * 2 + int_of k mod 2 = int_of k"
167 by (rule mod_div_equality)
168 have "int_of k mod 2 = 0 \<or> int_of k mod 2 = 1" by auto
169 from this show ?thesis
171 apply (insert 1) by (auto simp add: mult_ac)
175 code_instance code_numeral :: equal
176 (Haskell_Quickcheck -)
178 setup {* fold (Numeral.add_code @{const_name number_code_int_inst.number_of_code_int}
179 false Code_Printer.literal_numeral) ["Haskell_Quickcheck"] *}
181 code_const "0 \<Colon> code_int"
182 (Haskell_Quickcheck "0")
184 code_const "1 \<Colon> code_int"
185 (Haskell_Quickcheck "1")
187 code_const "minus \<Colon> code_int \<Rightarrow> code_int \<Rightarrow> code_int"
188 (Haskell_Quickcheck "(_/ -/ _)")
190 code_const div_mod_code_int
191 (Haskell_Quickcheck "divMod")
193 code_const "HOL.equal \<Colon> code_int \<Rightarrow> code_int \<Rightarrow> bool"
194 (Haskell_Quickcheck infix 4 "==")
196 code_const "op \<le> \<Colon> code_int \<Rightarrow> code_int \<Rightarrow> bool"
197 (Haskell_Quickcheck infix 4 "<=")
199 code_const "op < \<Colon> code_int \<Rightarrow> code_int \<Rightarrow> bool"
200 (Haskell_Quickcheck infix 4 "<")
203 (Haskell_Quickcheck "Int")
207 subsubsection {* Narrowing's deep representation of types and terms *}
209 datatype narrowing_type = SumOfProd "narrowing_type list list"
210 datatype narrowing_term = Var "code_int list" narrowing_type | Ctr code_int "narrowing_term list"
211 datatype 'a cons = C narrowing_type "(narrowing_term list => 'a) list"
213 primrec map_cons :: "('a => 'b) => 'a cons => 'b cons"
215 "map_cons f (C ty cs) = C ty (map (%c. f o c) cs)"
217 subsubsection {* From narrowing's deep representation of terms to @{theory Code_Evaluation}'s terms *}
219 class partial_term_of = typerep +
220 fixes partial_term_of :: "'a itself => narrowing_term => Code_Evaluation.term"
222 lemma partial_term_of_anything: "partial_term_of x nt \<equiv> t"
223 by (rule eq_reflection) (cases "partial_term_of x nt", cases t, simp)
225 subsubsection {* Auxilary functions for Narrowing *}
227 consts nth :: "'a list => code_int => 'a"
229 code_const nth (Haskell_Quickcheck infixl 9 "!!")
231 consts error :: "char list => 'a"
233 code_const error (Haskell_Quickcheck "error")
235 consts toEnum :: "code_int => char"
237 code_const toEnum (Haskell_Quickcheck "toEnum")
239 consts marker :: "char"
241 code_const marker (Haskell_Quickcheck "''\\0'")
243 subsubsection {* Narrowing's basic operations *}
245 type_synonym 'a narrowing = "code_int => 'a cons"
247 definition empty :: "'a narrowing"
249 "empty d = C (SumOfProd []) []"
251 definition cons :: "'a => 'a narrowing"
253 "cons a d = (C (SumOfProd [[]]) [(%_. a)])"
255 fun conv :: "(narrowing_term list => 'a) list => narrowing_term => 'a"
257 "conv cs (Var p _) = error (marker # map toEnum p)"
258 | "conv cs (Ctr i xs) = (nth cs i) xs"
260 fun nonEmpty :: "narrowing_type => bool"
262 "nonEmpty (SumOfProd ps) = (\<not> (List.null ps))"
264 definition "apply" :: "('a => 'b) narrowing => 'a narrowing => 'b narrowing"
267 (case f d of C (SumOfProd ps) cfs =>
268 case a (d - 1) of C ta cas =>
270 shallow = (d > 0 \<and> nonEmpty ta);
271 cs = [(%xs'. (case xs' of [] => undefined | x # xs => cf xs (conv cas x))). shallow, cf <- cfs]
272 in C (SumOfProd [ta # p. shallow, p <- ps]) cs)"
274 definition sum :: "'a narrowing => 'a narrowing => 'a narrowing"
277 (case a d of C (SumOfProd ssa) ca =>
278 case b d of C (SumOfProd ssb) cb =>
279 C (SumOfProd (ssa @ ssb)) (ca @ cb))"
282 assumes "a d = a' d" "b d = b' d" "d = d'"
283 shows "sum a b d = sum a' b' d'"
284 using assms unfolding sum_def by (auto split: cons.split narrowing_type.split)
287 assumes "f d = f' d" "(\<And>d'. 0 <= d' & d' < d ==> a d' = a' d')"
289 shows "apply f a d = apply f' a' d'"
292 have "int_of (of_int 0) < int_of d' ==> int_of (of_int 0) <= int_of (of_int (int_of d' - int_of (of_int 1)))"
293 by (simp add: of_int_inverse)
295 have "int_of (of_int (int_of d' - int_of (of_int 1))) < int_of d'"
296 by (simp add: of_int_inverse)
297 ultimately show ?thesis
298 unfolding apply_def by (auto split: cons.split narrowing_type.split simp add: Let_def)
301 subsubsection {* Narrowing generator type class *}
304 fixes narrowing :: "code_int => 'a cons"
306 datatype property = Universal narrowing_type "(narrowing_term => property)" "narrowing_term => Code_Evaluation.term" | Existential narrowing_type "(narrowing_term => property)" "narrowing_term => Code_Evaluation.term" | Property bool
308 (* FIXME: hard-wired maximal depth of 100 here *)
309 definition exists :: "('a :: {narrowing, partial_term_of} => property) => property"
311 "exists f = (case narrowing (100 :: code_int) of C ty cs => Existential ty (\<lambda> t. f (conv cs t)) (partial_term_of (TYPE('a))))"
313 definition "all" :: "('a :: {narrowing, partial_term_of} => property) => property"
315 "all f = (case narrowing (100 :: code_int) of C ty cs => Universal ty (\<lambda>t. f (conv cs t)) (partial_term_of (TYPE('a))))"
317 subsubsection {* class @{text is_testable} *}
319 text {* The class @{text is_testable} ensures that all necessary type instances are generated. *}
323 instance bool :: is_testable ..
325 instance "fun" :: ("{term_of, narrowing, partial_term_of}", is_testable) is_testable ..
327 definition ensure_testable :: "'a :: is_testable => 'a :: is_testable"
329 "ensure_testable f = f"
332 subsubsection {* Defining a simple datatype to represent functions in an incomplete and redundant way *}
334 datatype ('a, 'b) ffun = Constant 'b | Update 'a 'b "('a, 'b) ffun"
336 primrec eval_ffun :: "('a, 'b) ffun => 'a => 'b"
338 "eval_ffun (Constant c) x = c"
339 | "eval_ffun (Update x' y f) x = (if x = x' then y else eval_ffun f x)"
341 hide_type (open) ffun
342 hide_const (open) Constant Update eval_ffun
344 datatype 'b cfun = Constant 'b
346 primrec eval_cfun :: "'b cfun => 'a => 'b"
348 "eval_cfun (Constant c) y = c"
350 hide_type (open) cfun
351 hide_const (open) Constant eval_cfun
353 subsubsection {* Setting up the counterexample generator *}
355 use "Tools/Quickcheck/narrowing_generators.ML"
357 setup {* Narrowing_Generators.setup *}
359 subsection {* Narrowing for integers *}
362 definition drawn_from :: "'a list => 'a cons"
363 where "drawn_from xs = C (SumOfProd (map (%_. []) xs)) (map (%x y. x) xs)"
365 function around_zero :: "int => int list"
367 "around_zero i = (if i < 0 then [] else (if i = 0 then [0] else around_zero (i - 1) @ [i, -i]))"
368 by pat_completeness auto
369 termination by (relation "measure nat") auto
371 declare around_zero.simps[simp del]
373 lemma length_around_zero:
375 shows "length (around_zero i) = 2 * nat i + 1"
376 proof (induct rule: int_ge_induct[OF assms])
378 from 1 show ?case by (simp add: around_zero.simps)
382 by (simp add: around_zero.simps[of "i + 1"])
385 instantiation int :: narrowing
389 "narrowing_int d = (let (u :: _ => _ => unit) = conv; i = Quickcheck_Narrowing.int_of d in drawn_from (around_zero i))"
395 lemma [code, code del]: "partial_term_of (ty :: int itself) t == undefined"
396 by (rule partial_term_of_anything)+
399 "partial_term_of (ty :: int itself) (Var p t) == Code_Evaluation.Free (STR ''_'') (Typerep.Typerep (STR ''Int.int'') [])"
400 "partial_term_of (ty :: int itself) (Ctr i []) == (if i mod 2 = 0 then
401 Code_Evaluation.term_of (- (int_of i) div 2) else Code_Evaluation.term_of ((int_of i + 1) div 2))"
402 by (rule partial_term_of_anything)+
404 text {* Defining integers by positive and negative copy of naturals *}
406 datatype simple_int = Positive nat | Negative nat
408 primrec int_of_simple_int :: "simple_int => int"
410 "int_of_simple_int (Positive n) = int n"
411 | "int_of_simple_int (Negative n) = (-1 - int n)"
413 instantiation int :: narrowing
416 definition narrowing_int :: "code_int => int cons"
418 "narrowing_int d = map_cons int_of_simple_int ((narrowing :: simple_int narrowing) d)"
424 text {* printing the partial terms *}
427 "partial_term_of (ty :: int itself) t == Code_Evaluation.App (Code_Evaluation.Const (STR ''Quickcheck_Narrowing.int_of_simple_int'')
428 (Typerep.Typerep (STR ''fun'') [Typerep.Typerep (STR ''Quickcheck_Narrowing.simple_int'') [], Typerep.Typerep (STR ''Int.int'') []])) (partial_term_of (TYPE(simple_int)) t)"
429 by (rule partial_term_of_anything)
433 hide_type code_int narrowing_type narrowing_term cons property
434 hide_const int_of of_int nth error toEnum marker empty
435 C cons conv nonEmpty "apply" sum ensure_testable all exists
436 hide_const (open) Var Ctr
437 hide_fact empty_def cons_def conv.simps nonEmpty.simps apply_def sum_def ensure_testable_def all_def exists_def