1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
|
open Pp
open Util
open GenericLib
open SetLib
open CoqLib
open Feedback
let sizeM = gInject "QuickChick.Classes.size"
let succ_zero x = false_ind hole (succ_neq_zero_app hole x)
let base_ctrs ty_ctr ctrs = List.filter (fun (_, ty) -> isBaseBranch ty_ctr ty) ctrs
(* Produces the record of the sized typeclass *)
let sized_decl (types : (ty_ctr * ty_param list * ctr_rep list) list) : (ty_ctr -> var list -> coq_expr) * ((var * arg list * var * coq_expr * coq_expr) list) =
let impl_function_names : (ty_ctr * var) list =
List.map (fun (ty, _, _) ->
let type_name = ty_ctr_to_string ty in
let function_name = fresh_name ("size_impl_" ^ type_name) in
(ty, function_name)
) types in
let generate_size_function ((ty, ty_params, ctors) : (ty_ctr * ty_param list * ctr_rep list)) : var * arg list * var * coq_expr * coq_expr =
let function_name = List.assoc ty impl_function_names in
let coqTyParams = List.map gTyParam ty_params in
let full_type = gApp ~explicit:true (gTyCtr ty) coqTyParams in
let arg = fresh_name "x" in
let arg_type = full_type in
let return_type = gInject "Coq.Init.Datatypes.nat" in
let is_current_ty_crt = function
| TyCtr (ty_ctr', _) -> ty = ty_ctr'
| _ -> false in
(* Note: the size of an object do not take into consideration the size of
other objetcts it might contain, besides objects of its own type.
So actually we don't even need a fixpoint... but for now, we'll create one anyway
for simplicity. *)
let is_base_branch ty =
fold_ty' (fun b ty' -> b && not (is_current_ty_crt ty')) true ty in
let create_branch (ctr, ty) = (
ctr,
generate_names_from_type "p" ty,
if is_base_branch ty
then fun _ -> gInt 0
else fun vs ->
let opts = fold_ty_vars (fun _ v ty' ->
if is_current_ty_crt ty'
then [ gApp (gVar function_name) [gVar v] ]
else []
)
(fun l1 l2 -> l1 @ l2)
[] ty vs
in
gApp (gInject "S") [maximum opts]
) in
let body = gMatch (gVar arg) (List.map create_branch ctors) in
debug_coq_expr body;
(function_name, [gArg ~assumName:(gVar arg) ~assumType:arg_type ()], arg, return_type, body) in
let functions = List.map generate_size_function types in
(* returns {| size := size_impl_... |} *)
let instance_record ty_ctr _ivars : coq_expr =
let impl_function_name = List.assoc ty_ctr impl_function_names in
gRecord [("size", gVar impl_function_name)] in
(instance_record, functions)
let rec gen_args cty c_ctr n =
match cty with
| Arrow (ty1, ty2) ->
if sameTypeCtr c_ctr ty1
then
let x = Printf.sprintf "x%d" n in
let ih = Printf.sprintf "IHx%d" n in
let (args, ihargs) = gen_args ty2 c_ctr (n+1) in
(x :: args, x :: ih :: ihargs)
else
let x = Printf.sprintf "x%d" n in
let (args, ihargs) = gen_args ty2 c_ctr (n+1) in
(x :: args, x :: ihargs)
| _ -> ([], [])
let rec dropIH cty ty_ctr l =
match cty with
| Arrow (ty1, ty2) ->
(if sameTypeCtr ty_ctr ty1
then
match l with
| x :: _ihx :: l ->
let (l1, l2) = dropIH ty2 ty_ctr l in
(x :: l1, x :: l2)
| _ -> failwith "Internal: Wrong number of arguments"
else
match l with
| x :: l ->
let (l1, l2) = dropIH ty2 ty_ctr l in
(x :: l1, l2)
| _ -> failwith "Internal: Wrong number of arguments")
| _ -> ([], [])
let zeroEqProof ty_ctr ctrs (ind_scheme : coq_expr) size zeroType zeroSized iargs =
if List.is_empty ctrs then failwith "zeroEqProof call with no ctrs" else ();
(* Common helpers, refactor? *)
let coqTyCtr = gTyCtr ty_ctr in
let coqTyParams = List.map gVar (list_drop_every 2 iargs) in
let _full_dt = gApp ~explicit:true coqTyCtr coqTyParams in
let base_ctrs = base_ctrs ty_ctr ctrs in
let rec elim_set h ty n =
match ty with
| Arrow (_ty1, ty2) ->
let w' = Printf.sprintf "x%d" n in
let hw' = Printf.sprintf "Hx%d" n in
let hc1 = Printf.sprintf "Hl%d" n in
let hc2 = Printf.sprintf "Hr%d" n in
gMatch (gVar h)
[(injectCtr "ex_intro", [w'; hw'],
fun [_w'; hw'] ->
gMatch (gVar hw')
[(injectCtr "conj", [hc1; hc2],
fun [_hc1; hc2] ->
elim_set hc2 ty2 (n+1)
)]
)] [@ocaml.warning "-8"]
| _ -> discriminate (gVar h)
in
let rec elim_unions h ctrs =
match ctrs with
| [(_ctr, ty)] -> elim_set h ty 0
| (_ctr, ty) :: ctrs' ->
gMatch (gVar h)
[(injectCtr "or_introl", ["H1"],
fun [h1] -> elim_set h1 ty 0);
(injectCtr "or_intror", ["H1"],
fun [h1] -> elim_unions h1 ctrs')]
in
let rec intro_set ty ctr_args ctr acc =
match ty with
| Arrow (_ty1, ty2) ->
(match ctr_args with
| arg :: ctr_args' ->
gExIntro_impl arg (gConjIntro gI (intro_set ty2 ctr_args' ctr (arg :: acc)))
| [] -> failwith "Internal: wrong number of arguments")
| _ -> gEqRefl hole
(* (gApp ~explicit:true (gCtr ctr) (coqTyParams @ List.rev acc)) *)
in
let rec intro_unions ctrs args curr_ctr =
match ctrs with
| [(ctr, ty)] ->
if ctr = curr_ctr then
intro_set ty args ctr []
else
failwith "Internal: cannot find constructor"
| (ctr, ty) :: ctrs' ->
if ctr = curr_ctr then
gOrIntroL (intro_set ty args ctr [])
else
gOrIntroR (intro_unions ctrs' args curr_ctr)
in
let create_case (ctr, ty) =
let (_, iargs) = gen_args ty ty_ctr 0 in
gFun iargs (fun iargs ->
let (args, _) = dropIH ty ty_ctr iargs in
let elem = gApp ~explicit:true (gCtr ctr) (coqTyParams @ (List.map gVar args)) in
let lhs = gApp zeroSized [elem] in
let rhs = gEq (gApp size [elem]) (gInt 0) in
if isBaseBranch ty_ctr ty then
gConjIntro
(gFunTyped [("H1", lhs)] (fun [_h1] -> gEqRefl hole))
(gFunTyped [("H1", rhs)] (fun [_h1] -> intro_unions base_ctrs (List.map gVar args) ctr))
else
gConjIntro
(gFunTyped [("H1", lhs)] (fun [h1] -> elim_unions h1 base_ctrs))
(gFunTyped [("H1", rhs)] (fun [h1] -> succ_zero (gVar h1))))
in
let proofs = List.map create_case ctrs in
gApp ~explicit:true ind_scheme (coqTyParams @ (zeroType :: proofs))
let succEqProof ty_ctr ctrs (ind_scheme : coq_expr) succType succSized iargs =
(* Common helpers, refactor? *)
let coqTyCtr = gTyCtr ty_ctr in
let coqTyParams = List.map gVar (list_drop_every 2 iargs) in
let full_dt = gApp ~explicit:true coqTyCtr coqTyParams in
let _base_ctrs = base_ctrs ty_ctr ctrs in
let rec elim_set h leq ty n f ctr_flag size =
match ty with
| Arrow (ty1, ty2) ->
let w' = Printf.sprintf "x%d" n in
let hw' = Printf.sprintf "Hx%d" n in
let hc1 = Printf.sprintf "Hl%d" n in
let hc2 = Printf.sprintf "Hr%d" n in
gMatch (gVar h)
[(injectCtr "ex_intro", [w'; hw'],
fun [_w'; hw'] ->
gMatch (gVar hw')
[(injectCtr "conj", [hc1; hc2],
fun [hc1; hc2] ->
let leq' =
if sameTypeCtr ty_ctr ty1
then (gVar hc1) :: leq
else leq
in
elim_set hc2 leq' ty2 (n+1) f ctr_flag size
)]
)]
| _ ->
if ctr_flag then
let rec leq_proof = function
| [h] -> h
| h :: hs ->
gApp (gInject "max_lub_ssr") [hole; hole; gSucc (gVar size); h; leq_proof hs]
in
gMatch (gVar h) [(injectCtr "erefl", [], fun [] -> leq_proof (List.rev leq))]
else discriminate (gVar h)
in
let rec elim_unions h ctrs curr_ctr size =
match ctrs with
| [(ctr, ty)] -> elim_set h [] ty 0 (fun x -> x) (curr_ctr = ctr) size
| (ctr, ty) :: ctrs' ->
gMatch (gVar h)
[(injectCtr "or_introl", ["H1"],
fun [h1] -> elim_set h1 [] ty 0 (fun x -> x) (curr_ctr = ctr) size);
(injectCtr "or_intror", ["H1"],
fun [h1] -> elim_unions h1 ctrs' curr_ctr size)]
in
let rec intro_set leq ty ctr_args iargs ctr acc =
match ty with
| Arrow (ty1, ty2) ->
(match ctr_args with
| arg :: ctr_args' ->
let (leq_l, leq_r, iargs') =
if sameTypeCtr ty_ctr ty1
then
(match iargs with
| [_arg] ->
(leq, leq, [])
| _arg :: args ->
(gApp (gInject "max_lub_l_ssr") [hole; hole; hole; leq],
gApp (gInject "max_lub_r_ssr") [hole; hole; hole; leq],
args))
else (gI, leq, iargs)
in
gExIntro_impl arg
(gConjIntro leq_l (intro_set leq_r ty2 ctr_args' iargs' ctr (arg :: acc)))
| [] -> failwith "Internal: wrong number of arguments")
| _ -> gEqRefl (gApp ~explicit:true (gCtr ctr) (coqTyParams @ List.rev acc))
in
let rec intro_unions h ctrs args ihargs curr_ctr =
match ctrs with
| [(ctr, ty)] ->
if ctr = curr_ctr then
intro_set (gVar h) ty args ihargs ctr []
else
failwith "Internal: cannot find constructor"
| (ctr, ty) :: ctrs' ->
if ctr = curr_ctr then
gOrIntroL (intro_set (gVar h) ty args ihargs ctr [])
else
gOrIntroR (intro_unions h ctrs' args ihargs curr_ctr)
in
let create_case size (ctr, ty) =
let (_, iargs) = gen_args ty ty_ctr 0 in
gFun iargs (fun iargs ->
let (args, ihargs) = dropIH ty ty_ctr iargs in
let elem = gApp ~explicit:true (gCtr ctr) (coqTyParams @ (List.map gVar args)) in
let leq_size size = set_suchThat "x" full_dt (fun x -> gle (gApp sizeM [gVar x]) size) in
let lhs = gApp (gApp succSized [(leq_size (gVar size))]) [elem] in
let rhs = gApp (leq_size (gSucc (gVar size))) [elem] in
if isBaseBranch ty_ctr ty then
gConjIntro
(gFunTyped [("H1", lhs)] (fun [_h1] -> gApp (gInject "leq0n") [hole]))
(gFunTyped [("H1", rhs)] (fun [h1] -> intro_unions h1 ctrs (List.map gVar args) ihargs ctr))
else
gConjIntro
(gFunTyped [("H1", lhs)] (fun [h1] -> elim_unions h1 ctrs ctr size))
(gFunTyped [("H1", rhs)] (fun [h1] -> intro_unions h1 ctrs (List.map gVar args) ihargs ctr)))
in
let proofs size = List.map (create_case size) ctrs in
gFun
["size"]
(fun [size] -> gApp ~explicit:true ind_scheme (coqTyParams @ ((succType size) :: (proofs size))))
let sizeEqType ty_ctr ctrs ind_scheme iargs =
(* Common helpers, refactor? *)
let coqTyCtr = gTyCtr ty_ctr in
let coqTyParams = List.map gVar (list_drop_every 2 iargs) in
let full_dt = gApp ~explicit:true coqTyCtr coqTyParams in
let bases = base_ctrs ty_ctr ctrs in
(* Second reverse fold necessary *)
let create_branch set tps (ctr,ty) =
let rec aux i acc ty : coq_expr =
match ty with
| Arrow (ty1, ty2) ->
let fi = Printf.sprintf "f%d" i in
set_bigcup fi (if sameTypeCtr ty_ctr ty1 then set
else gFun [fi] (fun _ -> gInject "True"))
(fun f -> aux (i+1) (f::acc) ty2)
| _ -> set_singleton (gApp ~explicit:true (gCtr ctr) (tps @ (List.map gVar (List.rev acc)))) in
aux 0 [] ty in
let lhs set ctrs = set_unions (List.map (create_branch set coqTyParams) ctrs) in
let rhs size = set_suchThat "x" full_dt (fun x -> gEq (gApp sizeM [gVar x]) size) in
let zeroSized = lhs hole bases in
let succSized =
gFunWithArgs [gArg ~assumName:(gInject "set") ()]
(fun [set] -> lhs (gVar set) ctrs) in
let zeroType =
gFun ["f"] (fun [f] -> gIff (gApp zeroSized [gVar f]) (gApp (rhs (gInt 0)) [gVar f]))
in
let set_leq size = set_suchThat "x" full_dt (fun x -> gle (gApp sizeM [gVar x]) size) in
let succType size =
gFun ["f"] (fun [f] -> gIff (gApp (gApp succSized [set_leq (gVar size)]) [gVar f])
(gApp (set_leq (gSucc (gVar size))) [gVar f]))
in
let zeroSized_spec = zeroEqProof ty_ctr ctrs ind_scheme sizeM zeroType zeroSized iargs in
let succSized_spec = succEqProof ty_ctr ctrs ind_scheme succType succSized iargs in
msg_debug (str "zeroSized");
debug_coq_expr zeroSized;
msg_debug (str "succSized");
debug_coq_expr succSized;
msg_debug (str "zeroSized_spec");
debug_coq_expr zeroSized_spec;
debug_coq_expr succSized_spec;
gRecord [("zeroSized", zeroSized); ("succSized", succSized);
("zeroSized_spec", zeroSized_spec); ("succSized_spec", succSized_spec)]
|