src/HOL/Tools/Quickcheck/exhaustive_generators.ML
author bulwahn
Mon Dec 05 12:36:20 2011 +0100 (2011-12-05)
changeset 45763 3bb2bdf654f7
parent 45761 90028fd2f1fa
child 45923 473b744c23f2
permissions -rw-r--r--
random reporting compilation returns if counterexample is genuine or potentially spurious, and takes genuine_only option as argument
     1 (*  Title:      HOL/Tools/Quickcheck/exhaustive_generators.ML
     2     Author:     Lukas Bulwahn, TU Muenchen
     3 
     4 Exhaustive generators for various types.
     5 *)
     6 
     7 signature EXHAUSTIVE_GENERATORS =
     8 sig
     9   val compile_generator_expr:
    10     Proof.context -> (term * term list) list -> bool -> int list -> (bool * term list) option * Quickcheck.report option
    11   val compile_generator_exprs: Proof.context -> term list -> (int -> term list option) list
    12   val compile_validator_exprs: Proof.context -> term list -> (int -> bool) list
    13   val put_counterexample: (unit -> int -> bool -> int -> (bool * term list) option)
    14     -> Proof.context -> Proof.context
    15   val put_counterexample_batch: (unit -> (int -> term list option) list)
    16     -> Proof.context -> Proof.context
    17   val put_validator_batch: (unit -> (int -> bool) list) -> Proof.context -> Proof.context
    18   exception Counterexample of term list
    19   val smart_quantifier : bool Config.T
    20   val quickcheck_pretty : bool Config.T
    21   val setup_exhaustive_datatype_interpretation : theory -> theory
    22   val setup: theory -> theory
    23 end;
    24 
    25 structure Exhaustive_Generators : EXHAUSTIVE_GENERATORS =
    26 struct
    27 
    28 (* basics *)
    29 
    30 (** dynamic options **)
    31 
    32 val smart_quantifier = Attrib.setup_config_bool @{binding quickcheck_smart_quantifier} (K true)
    33 val fast = Attrib.setup_config_bool @{binding quickcheck_fast} (K false)
    34 val bounded_forall = Attrib.setup_config_bool @{binding quickcheck_bounded_forall} (K false)
    35 val full_support = Attrib.setup_config_bool @{binding quickcheck_full_support} (K true)
    36 val quickcheck_pretty = Attrib.setup_config_bool @{binding quickcheck_pretty} (K true)
    37  
    38 (** general term functions **)
    39 
    40 fun mk_measure f =
    41   let
    42     val Type ("fun", [T, @{typ nat}]) = fastype_of f 
    43   in
    44     Const (@{const_name Wellfounded.measure},
    45       (T --> @{typ nat}) --> HOLogic.mk_prodT (T, T) --> @{typ bool})
    46     $ f
    47   end
    48 
    49 fun mk_sumcases rT f (Type (@{type_name Sum_Type.sum}, [TL, TR])) =
    50   let
    51     val lt = mk_sumcases rT f TL
    52     val rt = mk_sumcases rT f TR
    53   in
    54     SumTree.mk_sumcase TL TR rT lt rt
    55   end
    56   | mk_sumcases _ f T = f T
    57 
    58 (** abstract syntax **)
    59 
    60 fun termifyT T = HOLogic.mk_prodT (T, @{typ "unit => Code_Evaluation.term"});
    61 
    62 val size = @{term "i :: code_numeral"}
    63 val size_pred = @{term "(i :: code_numeral) - 1"}
    64 val size_ge_zero = @{term "(i :: code_numeral) > 0"}
    65 
    66 fun mk_none_continuation (x, y) =
    67   let
    68     val (T as Type(@{type_name "option"}, [T'])) = fastype_of x
    69   in
    70     Const (@{const_name "Quickcheck_Exhaustive.orelse"}, T --> T --> T) $ x $ y
    71   end
    72 
    73 fun mk_unit_let (x, y) =
    74   Const (@{const_name "Let"}, @{typ "unit => (unit => unit) => unit"}) $ x $ absdummy @{typ unit} y
    75 
    76 fun mk_if (b, t, e) =
    77   let
    78     val T = fastype_of t
    79   in Const (@{const_name "HOL.If"}, @{typ bool} --> T --> T --> T) $ b $ t $ e end
    80 
    81 (* handling inductive datatypes *)
    82 
    83 (** constructing generator instances **)
    84 
    85 exception FUNCTION_TYPE;
    86 
    87 exception Counterexample of term list
    88 
    89 val resultT =  @{typ "(bool * term list) option"};
    90 
    91 val exhaustiveN = "exhaustive";
    92 val full_exhaustiveN = "full_exhaustive";
    93 val fast_exhaustiveN = "fast_exhaustive";
    94 val bounded_forallN = "bounded_forall";
    95 
    96 fun fast_exhaustiveT T = (T --> @{typ unit})
    97   --> @{typ code_numeral} --> @{typ unit}
    98 
    99 fun exhaustiveT T = (T --> resultT) --> @{typ code_numeral} --> resultT
   100 
   101 fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool}
   102 
   103 fun full_exhaustiveT T = (termifyT T --> resultT) --> @{typ code_numeral} --> resultT
   104 
   105 fun check_allT T = (termifyT T --> resultT) --> resultT
   106 
   107 fun mk_equation_terms generics (descr, vs, Ts) =
   108   let
   109     val (mk_call, mk_aux_call, mk_consexpr, mk_rhs, test_function, exhaustives) = generics
   110     val rhss =
   111       Datatype_Aux.interpret_construction descr vs
   112         { atyp = mk_call, dtyp = mk_aux_call }
   113       |> (map o apfst) Type
   114       |> map (fn (T, cs) => map (mk_consexpr T) cs)
   115       |> map mk_rhs
   116     val lhss = map2 (fn t => fn T => t $ test_function T $ size) exhaustives Ts
   117   in
   118     map (HOLogic.mk_Trueprop o HOLogic.mk_eq) (lhss ~~ rhss)
   119   end
   120 
   121 fun gen_mk_call c T =  (T, fn t => c T $ absdummy T t $ size_pred)
   122 
   123 fun gen_mk_aux_call functerms fTs (k, _) (tyco, Ts) =
   124   let
   125     val T = Type (tyco, Ts)
   126     val _ = if not (null fTs) then raise FUNCTION_TYPE else ()
   127   in
   128    (T, fn t => nth functerms k $ absdummy T t $ size_pred)
   129   end
   130 
   131 fun gen_mk_consexpr test_function functerms simpleT (c, xs) =
   132   let
   133     val (Ts, fns) = split_list xs
   134     val constr = Const (c, Ts ---> simpleT)
   135     val bounds = map Bound (((length xs) - 1) downto 0)
   136     val term_bounds = map (fn x => Bound (2 * x)) (((length xs) - 1) downto 0)
   137     val start_term = test_function simpleT $ list_comb (constr, bounds)
   138   in fold_rev (fn f => fn t => f t) fns start_term end
   139       
   140 fun mk_fast_equations functerms =
   141   let
   142     fun test_function T = Free ("f", T --> @{typ "unit"})
   143     val mk_call = gen_mk_call (fn T =>
   144       Const (@{const_name "Quickcheck_Exhaustive.fast_exhaustive_class.fast_exhaustive"},
   145         fast_exhaustiveT T))
   146     val mk_aux_call = gen_mk_aux_call functerms
   147     val mk_consexpr = gen_mk_consexpr test_function functerms
   148     fun mk_rhs exprs = @{term "If :: bool => unit => unit => unit"}
   149         $ size_ge_zero $ (foldr1 mk_unit_let exprs) $ @{term "()"}
   150   in
   151     mk_equation_terms (mk_call, mk_aux_call, mk_consexpr, mk_rhs, test_function, functerms)
   152   end
   153 
   154 fun mk_equations functerms =
   155   let
   156     fun test_function T = Free ("f", T --> resultT)
   157     val mk_call = gen_mk_call (fn T =>
   158       Const (@{const_name "Quickcheck_Exhaustive.exhaustive_class.exhaustive"}, exhaustiveT T))
   159     val mk_aux_call = gen_mk_aux_call functerms
   160     val mk_consexpr = gen_mk_consexpr test_function functerms
   161     fun mk_rhs exprs =
   162       mk_if (size_ge_zero, foldr1 mk_none_continuation exprs, Const (@{const_name "None"}, resultT))
   163   in
   164     mk_equation_terms (mk_call, mk_aux_call, mk_consexpr, mk_rhs, test_function, functerms)
   165   end
   166 
   167 fun mk_bounded_forall_equations functerms =
   168   let
   169     fun test_function T = Free ("P", T --> @{typ bool})
   170     val mk_call = gen_mk_call (fn T =>
   171       Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"},
   172         bounded_forallT T))
   173     val mk_aux_call = gen_mk_aux_call functerms
   174     val mk_consexpr = gen_mk_consexpr test_function functerms
   175     fun mk_rhs exprs =
   176       mk_if (size_ge_zero, foldr1 HOLogic.mk_conj exprs, @{term "True"})
   177   in
   178     mk_equation_terms (mk_call, mk_aux_call, mk_consexpr, mk_rhs, test_function, functerms)
   179   end
   180   
   181 fun mk_full_equations functerms =
   182   let
   183     fun test_function T = Free ("f", termifyT T --> resultT)
   184     fun split_const T = HOLogic.split_const (T, @{typ "unit => Code_Evaluation.term"}, resultT)
   185     fun mk_call T =
   186       let
   187         val full_exhaustive =
   188           Const (@{const_name "Quickcheck_Exhaustive.full_exhaustive_class.full_exhaustive"},
   189             full_exhaustiveT T)
   190       in                                   
   191         (T, fn t => full_exhaustive $
   192           (split_const T $ absdummy T (absdummy @{typ "unit => Code_Evaluation.term"} t)) $ size_pred)
   193       end
   194     fun mk_aux_call fTs (k, _) (tyco, Ts) =
   195       let
   196         val T = Type (tyco, Ts)
   197         val _ = if not (null fTs) then raise FUNCTION_TYPE else ()
   198       in
   199         (T, fn t => nth functerms k $
   200           (split_const T $ absdummy T (absdummy @{typ "unit => Code_Evaluation.term"} t)) $ size_pred)
   201       end
   202     fun mk_consexpr simpleT (c, xs) =
   203       let
   204         val (Ts, fns) = split_list xs
   205         val constr = Const (c, Ts ---> simpleT)
   206         val bounds = map (fn x => Bound (2 * x + 1)) (((length xs) - 1) downto 0)
   207         val term_bounds = map (fn x => Bound (2 * x)) (((length xs) - 1) downto 0)
   208         val Eval_App = Const ("Code_Evaluation.App", HOLogic.termT --> HOLogic.termT --> HOLogic.termT)
   209         val Eval_Const = Const ("Code_Evaluation.Const", HOLogic.literalT --> @{typ typerep} --> HOLogic.termT)
   210         val term = fold (fn u => fn t => Eval_App $ t $ (u $ @{term "()"}))
   211           bounds (Eval_Const $ HOLogic.mk_literal c $ HOLogic.mk_typerep (Ts ---> simpleT))
   212         val start_term = test_function simpleT $ 
   213         (HOLogic.pair_const simpleT @{typ "unit => Code_Evaluation.term"}
   214           $ (list_comb (constr, bounds)) $ absdummy @{typ unit} term)
   215       in fold_rev (fn f => fn t => f t) fns start_term end
   216     fun mk_rhs exprs =
   217       mk_if (size_ge_zero, foldr1 mk_none_continuation exprs,
   218         Const (@{const_name "None"}, resultT))
   219   in
   220     mk_equation_terms (mk_call, mk_aux_call, mk_consexpr, mk_rhs, test_function, functerms)
   221   end
   222   
   223 (** foundational definition with the function package **)
   224 
   225 val less_int_pred = @{lemma "i > 0 ==> Code_Numeral.nat_of ((i :: code_numeral) - 1) < Code_Numeral.nat_of i" by auto}
   226 
   227 fun mk_single_measure T = HOLogic.mk_comp (@{term "Code_Numeral.nat_of"},
   228     Const (@{const_name "Product_Type.snd"}, T --> @{typ "code_numeral"}))
   229 
   230 fun mk_termination_measure T =
   231   let
   232     val T' = fst (HOLogic.dest_prodT (HOLogic.dest_setT T))
   233   in
   234     mk_measure (mk_sumcases @{typ nat} mk_single_measure T')
   235   end
   236 
   237 fun termination_tac ctxt = 
   238   Function_Relation.relation_tac ctxt mk_termination_measure 1
   239   THEN rtac @{thm wf_measure} 1
   240   THEN (REPEAT_DETERM (Simplifier.asm_full_simp_tac 
   241     (HOL_basic_ss addsimps [@{thm in_measure}, @{thm o_def}, @{thm snd_conv},
   242      @{thm nat_mono_iff}, less_int_pred] @ @{thms sum.cases}) 1))
   243 
   244 (** instantiating generator classes **)
   245   
   246 fun contains_recursive_type_under_function_types xs =
   247   exists (fn (_, (_, _, cs)) => cs |> exists (snd #> exists (fn dT =>
   248     (case Datatype_Aux.strip_dtyp dT of (_ :: _, Datatype.DtRec _) => true | _ => false)))) xs
   249     
   250 fun instantiate_datatype (name, constprfx, sort, mk_equations, mk_T, argnames)
   251     config descr vs tycos prfx (names, auxnames) (Ts, Us) thy =
   252   if not (contains_recursive_type_under_function_types descr) then
   253     let
   254       val _ = Datatype_Aux.message config ("Creating " ^ name ^ "...")
   255       val fullnames = map (prefix (constprfx ^ "_")) (names @ auxnames)
   256     in
   257       thy
   258       |> Class.instantiation (tycos, vs, sort)
   259       |> Quickcheck_Common.define_functions
   260           (fn functerms => mk_equations functerms (descr, vs, Ts @ Us), NONE)
   261           prfx argnames fullnames (map mk_T (Ts @ Us))
   262       |> Class.prove_instantiation_exit (K (Class.intro_classes_tac []))
   263     end
   264   else
   265     (Datatype_Aux.message config
   266       ("Creation of " ^ name ^ " failed because the datatype is recursive under a function type");
   267     thy)
   268 
   269 val instantiate_bounded_forall_datatype = instantiate_datatype
   270  ("bounded universal quantifiers", bounded_forallN, @{sort bounded_forall},
   271    mk_bounded_forall_equations, bounded_forallT, ["P", "i"]);
   272 
   273 val instantiate_fast_exhaustive_datatype = instantiate_datatype 
   274  ("fast exhaustive generators", fast_exhaustiveN, @{sort fast_exhaustive},
   275    mk_fast_equations, fast_exhaustiveT, ["f", "i"])
   276 
   277 val instantiate_exhaustive_datatype = instantiate_datatype  
   278   ("exhaustive generators", exhaustiveN, @{sort exhaustive},
   279     mk_equations, exhaustiveT, ["f", "i"])
   280 
   281 val instantiate_full_exhaustive_datatype = instantiate_datatype
   282   ("full exhaustive generators", full_exhaustiveN, @{sort full_exhaustive},
   283     mk_full_equations, full_exhaustiveT, ["f", "i"])
   284 
   285 (* building and compiling generator expressions *)
   286 
   287 
   288 fun mk_test_term lookup mk_closure mk_if none_t return ctxt =
   289   let
   290     fun mk_naive_test_term t =
   291       fold_rev mk_closure (map lookup (Term.add_free_names t []))
   292         (mk_if (t, none_t, return) true)
   293     fun mk_smart_test_term' concl bound_vars assms genuine =
   294       let
   295         fun vars_of t = subtract (op =) bound_vars (Term.add_free_names t [])
   296         val (vars, check) =
   297           case assms of [] => (vars_of concl, (concl, none_t, return))
   298             | assm :: assms => (vars_of assm, (HOLogic.mk_not assm, none_t, 
   299                 mk_smart_test_term' concl (union (op =) (vars_of assm) bound_vars) assms))
   300       in
   301         fold_rev mk_closure (map lookup vars) (mk_if check genuine)
   302       end
   303     val mk_smart_test_term =
   304       Quickcheck_Common.strip_imp #> (fn (assms, concl) => mk_smart_test_term' concl [] assms true)
   305   in
   306     if Config.get ctxt smart_quantifier then mk_smart_test_term else mk_naive_test_term 
   307   end
   308 
   309 fun mk_fast_generator_expr ctxt (t, eval_terms) =
   310   let
   311     val thy = Proof_Context.theory_of ctxt
   312     val ctxt' = Variable.auto_fixes t ctxt
   313     val names = Term.add_free_names t []
   314     val frees = map Free (Term.add_frees t [])
   315     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   316     val ([depth_name], ctxt'') = Variable.variant_fixes ["depth"] ctxt'
   317     val depth = Free (depth_name, @{typ code_numeral})
   318     fun return _ = @{term "throw_Counterexample :: term list => unit"} $
   319       (HOLogic.mk_list @{typ "term"}
   320         (map (fn t => HOLogic.mk_term_of (fastype_of t) t) (frees @ eval_terms)))
   321     fun mk_exhaustive_closure (free as Free (_, T)) t =
   322       Const (@{const_name "Quickcheck_Exhaustive.fast_exhaustive_class.fast_exhaustive"},
   323         fast_exhaustiveT T)
   324         $ lambda free t $ depth
   325     val none_t = @{term "()"}
   326     fun mk_safe_if (cond, then_t, else_t) genuine = mk_if (cond, then_t, else_t genuine)
   327     val mk_test_term = mk_test_term lookup mk_exhaustive_closure mk_safe_if none_t return ctxt 
   328   in lambda depth (@{term "catch_Counterexample :: unit => term list option"} $ mk_test_term t) end
   329 
   330 fun mk_unknown_term T = HOLogic.reflect_term (Const ("Quickcheck_Exhaustive.unknown", T))
   331 
   332 fun mk_safe_term t = @{term "Quickcheck.catch_match :: term => term => term"}
   333       $ (HOLogic.mk_term_of (fastype_of t) t) $ mk_unknown_term (fastype_of t)    
   334 
   335 fun mk_return t genuine = @{term "Some :: bool * term list => (bool * term list) option"} $
   336   (HOLogic.pair_const @{typ bool} @{typ "term list"} $ Quickcheck_Common.reflect_bool genuine $ t)
   337 
   338 fun mk_generator_expr ctxt (t, eval_terms) =
   339   let
   340     val thy = Proof_Context.theory_of ctxt
   341     val ctxt' = Variable.auto_fixes t ctxt
   342     val names = Term.add_free_names t []
   343     val frees = map Free (Term.add_frees t [])
   344     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   345     val ([depth_name, genuine_only_name], ctxt'') =
   346       Variable.variant_fixes ["depth", "genuine_only"] ctxt'
   347     val depth = Free (depth_name, @{typ code_numeral})
   348     val genuine_only = Free (genuine_only_name, @{typ bool}) 
   349     val return = mk_return (HOLogic.mk_list @{typ "term"}
   350         (map (fn t => HOLogic.mk_term_of (fastype_of t) t) frees @ map mk_safe_term eval_terms))
   351     fun mk_exhaustive_closure (free as Free (_, T)) t =
   352       Const (@{const_name "Quickcheck_Exhaustive.exhaustive_class.exhaustive"}, exhaustiveT T)
   353         $ lambda free t $ depth
   354     val none_t = Const (@{const_name "None"}, resultT)
   355     val mk_if = Quickcheck_Common.mk_safe_if genuine_only none_t
   356     val mk_test_term = mk_test_term lookup mk_exhaustive_closure mk_if none_t return ctxt
   357   in lambda genuine_only (lambda depth (mk_test_term t)) end
   358 
   359 fun mk_full_generator_expr ctxt (t, eval_terms) =
   360   let
   361     val thy = Proof_Context.theory_of ctxt
   362     val ctxt' = Variable.auto_fixes t ctxt
   363     val names = Term.add_free_names t []
   364     val frees = map Free (Term.add_frees t [])
   365     val ([depth_name, genuine_only_name], ctxt'') =
   366       Variable.variant_fixes ["depth", "genuine_only"] ctxt'
   367     val (term_names, ctxt''') = Variable.variant_fixes (map (prefix "t_") names) ctxt''
   368     val depth = Free (depth_name, @{typ code_numeral})
   369     val genuine_only = Free (depth_name, @{typ bool})    
   370     val term_vars = map (fn n => Free (n, @{typ "unit => term"})) term_names
   371     fun lookup v = the (AList.lookup (op =) (names ~~ (frees ~~ term_vars)) v)
   372     val return = mk_return (HOLogic.mk_list @{typ term}
   373           (map (fn v => v $ @{term "()"}) term_vars @ map mk_safe_term eval_terms))
   374     fun mk_exhaustive_closure (free as Free (_, T), term_var) t =
   375       if Sign.of_sort thy (T, @{sort enum}) then
   376         Const (@{const_name "Quickcheck_Exhaustive.check_all_class.check_all"}, check_allT T)
   377           $ (HOLogic.split_const (T, @{typ "unit => term"}, resultT)
   378             $ lambda free (lambda term_var t))
   379       else
   380         Const (@{const_name "Quickcheck_Exhaustive.full_exhaustive_class.full_exhaustive"}, full_exhaustiveT T)
   381           $ (HOLogic.split_const (T, @{typ "unit => term"}, resultT)
   382             $ lambda free (lambda term_var t)) $ depth
   383     val none_t = Const (@{const_name "None"}, resultT)
   384     val mk_if = Quickcheck_Common.mk_safe_if genuine_only none_t
   385     val mk_test_term = mk_test_term lookup mk_exhaustive_closure mk_if none_t return ctxt
   386   in lambda genuine_only (lambda depth (mk_test_term t)) end
   387 
   388 fun mk_parametric_generator_expr mk_generator_expr =
   389   Quickcheck_Common.gen_mk_parametric_generator_expr 
   390     ((mk_generator_expr,
   391       absdummy @{typ bool} (absdummy @{typ "code_numeral"} (Const (@{const_name "None"}, resultT)))),
   392       @{typ bool} --> @{typ "code_numeral"} --> resultT)
   393 
   394 fun mk_validator_expr ctxt t =
   395   let
   396     fun bounded_forallT T = (T --> @{typ bool}) --> @{typ code_numeral} --> @{typ bool}
   397     val thy = Proof_Context.theory_of ctxt
   398     val ctxt' = Variable.auto_fixes t ctxt
   399     val names = Term.add_free_names t []
   400     val frees = map Free (Term.add_frees t [])
   401     fun lookup v = the (AList.lookup (op =) (names ~~ frees) v)
   402     val ([depth_name], ctxt'') = Variable.variant_fixes ["depth"] ctxt'
   403     val depth = Free (depth_name, @{typ code_numeral})
   404     fun mk_bounded_forall (Free (s, T)) t =
   405       Const (@{const_name "Quickcheck_Exhaustive.bounded_forall_class.bounded_forall"}, bounded_forallT T)
   406         $ lambda (Free (s, T)) t $ depth
   407     fun mk_safe_if (cond, then_t, else_t) genuine = mk_if (cond, then_t, else_t genuine)
   408     val mk_test_term =
   409       mk_test_term lookup mk_bounded_forall mk_safe_if @{term True} (K @{term False}) ctxt
   410   in lambda depth (mk_test_term t) end
   411 
   412 
   413 fun mk_bounded_forall_generator_expr ctxt (t, eval_terms) = 
   414   let
   415     val frees = Term.add_free_names t []
   416     val dummy_term = @{term "Code_Evaluation.Const (STR ''dummy_pattern'')
   417       (Typerep.Typerep (STR ''dummy'') [])"}
   418     val return = @{term "Some :: term list => term list option"} $
   419       (HOLogic.mk_list @{typ "term"}
   420         (replicate (length frees + length eval_terms) dummy_term))
   421     val wrap = absdummy @{typ bool}
   422       (@{term "If :: bool => term list option => term list option => term list option"} $
   423         Bound 0 $ @{term "None :: term list option"} $ return)
   424   in HOLogic.mk_comp (wrap, mk_validator_expr ctxt t) end
   425   
   426 (** generator compiliation **)
   427 
   428 structure Counterexample = Proof_Data
   429 (
   430   type T = unit -> int -> bool -> int -> (bool * term list) option
   431   (* FIXME avoid user error with non-user text *)
   432   fun init _ () = error "Counterexample"
   433 );
   434 val put_counterexample = Counterexample.put;
   435 
   436 structure Counterexample_Batch = Proof_Data
   437 (
   438   type T = unit -> (int -> term list option) list
   439   (* FIXME avoid user error with non-user text *)
   440   fun init _ () = error "Counterexample"
   441 );
   442 val put_counterexample_batch = Counterexample_Batch.put;
   443 
   444 structure Validator_Batch = Proof_Data
   445 (
   446   type T = unit -> (int -> bool) list
   447   (* FIXME avoid user error with non-user text *)
   448   fun init _ () = error "Counterexample"
   449 );
   450 val put_validator_batch = Validator_Batch.put;
   451 
   452 
   453 val target = "Quickcheck";
   454 
   455 fun compile_generator_expr ctxt ts =
   456   let
   457     val thy = Proof_Context.theory_of ctxt
   458     val mk_generator_expr = 
   459       if Config.get ctxt fast then mk_fast_generator_expr
   460       else if Config.get ctxt bounded_forall then mk_bounded_forall_generator_expr
   461       else if Config.get ctxt full_support then mk_full_generator_expr else mk_generator_expr
   462     val t' = mk_parametric_generator_expr mk_generator_expr ctxt ts;
   463     val compile = Code_Runtime.dynamic_value_strict
   464       (Counterexample.get, put_counterexample, "Exhaustive_Generators.put_counterexample")
   465       thy (SOME target) (fn proc => fn g =>
   466         fn card => fn genuine_only => fn size => g card genuine_only size
   467           |> (Option.map o apsnd o map) proc) t' []
   468   in
   469     fn genuine_only => fn [card, size] => rpair NONE (compile card genuine_only size |> 
   470       (if Config.get ctxt quickcheck_pretty then
   471         Option.map (apsnd (map Quickcheck_Common.post_process_term)) else I))
   472   end;
   473 
   474 fun compile_generator_exprs ctxt ts =
   475   let
   476     val thy = Proof_Context.theory_of ctxt
   477     val ts' = map (fn t => mk_generator_expr ctxt (t, [])) ts;
   478     val compiles = Code_Runtime.dynamic_value_strict
   479       (Counterexample_Batch.get, put_counterexample_batch,
   480         "Exhaustive_Generators.put_counterexample_batch")
   481       thy (SOME target) (fn proc => map (fn g => g #> (Option.map o map) proc))
   482       (HOLogic.mk_list @{typ "code_numeral => term list option"} ts') []
   483   in
   484     map (fn compile => fn size => compile size
   485       |> Option.map (map Quickcheck_Common.post_process_term)) compiles
   486   end;
   487 
   488 fun compile_validator_exprs ctxt ts =
   489   let
   490     val thy = Proof_Context.theory_of ctxt
   491     val ts' = map (mk_validator_expr ctxt) ts
   492   in
   493     Code_Runtime.dynamic_value_strict
   494       (Validator_Batch.get, put_validator_batch, "Exhaustive_Generators.put_validator_batch")
   495       thy (SOME target) (K I) (HOLogic.mk_list @{typ "code_numeral => bool"} ts') []
   496   end;
   497 
   498 val test_goals = Quickcheck_Common.generator_test_goal_terms ("exhaustive", compile_generator_expr);
   499   
   500 (* setup *)
   501 
   502 val setup_exhaustive_datatype_interpretation =
   503   Datatype.interpretation (Quickcheck_Common.ensure_sort_datatype
   504       (((@{sort typerep}, @{sort term_of}), @{sort exhaustive}), instantiate_exhaustive_datatype))
   505 
   506 val active = Attrib.setup_config_bool @{binding quickcheck_exhaustive_active} (K true);
   507 
   508 val setup =
   509   Datatype.interpretation (Quickcheck_Common.ensure_sort_datatype
   510       (((@{sort typerep}, @{sort term_of}), @{sort full_exhaustive}), instantiate_full_exhaustive_datatype))
   511 (* #> Datatype.interpretation (Quickcheck_Common.ensure_sort_datatype
   512       (((@{sort typerep}, @{sort term_of}), @{sort exhaustive}), instantiate_exhaustive_datatype))
   513   #> Datatype.interpretation (Quickcheck_Common.ensure_sort_datatype
   514       (((@{sort typerep}, @{sort term_of}), @{sort fast_exhaustive}), instantiate_fast_exhaustive_datatype))
   515   #> Datatype.interpretation (Quickcheck_Common.ensure_sort_datatype
   516       (((@{sort type}, @{sort type}), @{sort bounded_forall}), instantiate_bounded_forall_datatype))*)
   517   #> Context.theory_map (Quickcheck.add_tester ("exhaustive", (active, test_goals)))
   518   #> Context.theory_map (Quickcheck.add_batch_generator ("exhaustive", compile_generator_exprs))
   519   #> Context.theory_map (Quickcheck.add_batch_validator ("exhaustive", compile_validator_exprs));
   520 
   521 end;