File: sized.ml

package info (click to toggle)
coq-quickchick 2.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,432 kB
  • sloc: ml: 4,367; ansic: 789; makefile: 388; sh: 27; python: 4; perl: 2; lisp: 2
file content (363 lines) | stat: -rw-r--r-- 12,810 bytes parent folder | download | duplicates (4)
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)]