]> matita.cs.unibo.it Git - fireball-separation.git/blob - ocaml/simple.ml
c86796e56c8d2a690787cba48f40a536c08b1232
[fireball-separation.git] / ocaml / simple.ml
1 let (++) f g x = f (g x);;\r
2 let id x = x;;\r
3 let rec fold_nat f x n = if n = 0 then x else f (fold_nat f x (n-1)) n ;;\r
4 \r
5 let print_hline = Console.print_hline;;\r
6 \r
7 open Pure\r
8 \r
9 type var = int;;\r
10 type t =\r
11  | V of var\r
12  | A of t * t\r
13  | L of t\r
14  | B (* bottom *)\r
15  | C (* constant *)\r
16 ;;\r
17 \r
18 let delta = L(A(V 0, V 0));;\r
19 \r
20 let rec is_stuck = function\r
21  | C -> true\r
22  | A(t,_) -> is_stuck t\r
23  | _ -> false\r
24 ;;\r
25 \r
26 let eta_eq' =\r
27  let rec aux l1 l2 t1 t2 = match t1, t2 with\r
28   | _, _ when is_stuck t1 || is_stuck t2 -> true\r
29   | L t1, L t2 -> aux l1 l2 t1 t2\r
30   | L t1, t2 -> aux l1 (l2+1) t1 t2\r
31   | t1, L t2 -> aux (l1+1) l2 t1 t2\r
32   | V a, V b -> a + l1 = b + l2\r
33   | A(t1,t2), A(u1,u2) -> aux l1 l2 t1 u1 && aux l1 l2 t2 u2\r
34   | _, _ -> false\r
35  in aux ;;\r
36 let eta_eq = eta_eq' 0 0;;\r
37 \r
38 (* is arg1 eta-subterm of arg2 ? *)\r
39 let eta_subterm u =\r
40  let rec aux lev t = if t = C then false else (eta_eq' lev 0 u t || match t with\r
41  | L t -> aux (lev+1) t\r
42  | A(t1, t2) -> aux lev t1 || aux lev t2\r
43  | _ -> false) in\r
44  aux 0\r
45 ;;\r
46 \r
47 (* does NOT lift the argument *)\r
48 let mk_lams = fold_nat (fun x _ -> L x) ;;\r
49 \r
50 let string_of_t =\r
51   let string_of_bvar =\r
52    let bound_vars = ["x"; "y"; "z"; "w"; "q"] in\r
53    let bvarsno = List.length bound_vars in\r
54    fun nn -> if nn < bvarsno then List.nth bound_vars nn else "x" ^ (string_of_int (nn - bvarsno + 1)) in\r
55   let rec string_of_term_w_pars level = function\r
56     | V v -> if v >= level then "`" ^ string_of_int (v-level) else\r
57        string_of_bvar (level - v-1)\r
58     | C -> "C"\r
59     | A _\r
60     | L _ as t -> "(" ^ string_of_term_no_pars level t ^ ")"\r
61     | B -> "BOT"\r
62   and string_of_term_no_pars_app level = function\r
63     | A(t1,t2) -> string_of_term_no_pars_app level t1 ^ " " ^ string_of_term_w_pars level t2\r
64     | _ as t -> string_of_term_w_pars level t\r
65   and string_of_term_no_pars level = function\r
66     | L t -> "λ" ^ string_of_bvar level ^ ". " ^ string_of_term_no_pars (level+1) t\r
67     | _ as t -> string_of_term_no_pars_app level t\r
68   in string_of_term_no_pars 0\r
69 ;;\r
70 \r
71 type problem = {\r
72    orig_freshno: int\r
73  ; freshno : int\r
74  ; div : t\r
75  ; conv : t\r
76  ; sigma : (var * t) list (* substitutions *)\r
77  ; stepped : var list\r
78  ; phase : [`One | `Two] (* :'( *)\r
79 }\r
80 \r
81 let string_of_problem p =\r
82  let lines = [\r
83   "[stepped] " ^ String.concat " " (List.map string_of_int p.stepped);\r
84   "[DV] " ^ string_of_t p.div;\r
85   "[CV] " ^ string_of_t p.conv;\r
86  ] in\r
87  String.concat "\n" lines\r
88 ;;\r
89 \r
90 exception Done of (var * t) list (* substitution *);;\r
91 exception Fail of int * string;;\r
92 \r
93 let problem_fail p reason =\r
94  print_endline "!!!!!!!!!!!!!!! FAIL !!!!!!!!!!!!!!!";\r
95  print_endline (string_of_problem p);\r
96  raise (Fail (-1, reason))\r
97 ;;\r
98 \r
99 let freshvar ({freshno} as p) =\r
100  {p with freshno=freshno+1}, freshno+1\r
101 ;;\r
102 \r
103 (* CSC: rename? is an applied C an inert?\r
104    is_inert and get_inert work inconsistently *)\r
105 let rec is_inert =\r
106  function\r
107  | A(t,_) -> is_inert t\r
108  | V _ -> true\r
109  | C\r
110  | L _ | B -> false\r
111 ;;\r
112 \r
113 let rec is_constant =\r
114  function\r
115     C -> true\r
116   | V _ -> false\r
117   | B -> assert false\r
118   | A(t,_)\r
119   | L t -> is_constant t\r
120 ;;\r
121 \r
122 let rec get_inert = function\r
123  | V _ | C as t -> (t,0)\r
124  | A(t, _) -> let hd,args = get_inert t in hd,args+1\r
125  | _ -> assert false\r
126 ;;\r
127 \r
128 let args_of_inert =\r
129  let rec aux acc =\r
130   function\r
131    | V _ | C -> acc\r
132    | A(t, a) -> aux (a::acc) t\r
133    | _ -> assert false\r
134  in\r
135   aux []\r
136 ;;\r
137 \r
138 (* precomputes the number of leading lambdas in a term,\r
139    after replacing _v_ w/ a term starting with n lambdas *)\r
140 let rec no_leading_lambdas v n = function\r
141  | L t -> 1 + no_leading_lambdas (v+1) n t\r
142  | A _ as t -> let v', m = get_inert t in if V v = v' then max 0 (n - m) else 0\r
143  | V v' -> if v = v' then n else 0\r
144  | B | C -> 0\r
145 ;;\r
146 \r
147 let rec subst level delift sub =\r
148  function\r
149  | V v -> if v = level + fst sub then lift level (snd sub) else V (if delift && v > level then v-1 else v)\r
150  | L t -> let t = subst (level + 1) delift sub t in if t = B then B else L t\r
151  | A (t1,t2) ->\r
152   let t1 = subst level delift sub t1 in\r
153   let t2 = subst level delift sub t2 in\r
154   mk_app t1 t2\r
155  | C | B as t -> t\r
156 and mk_app t1 t2 = if t2 = B || (t1 = delta && t2 = delta) then B\r
157  else match t1 with\r
158  | B -> B\r
159  | L t1 -> subst 0 true (0, t2) t1\r
160  | _ -> A (t1, t2)\r
161 and lift n =\r
162  let rec aux lev =\r
163   function\r
164   | V m -> V (if m >= lev then m + n else m)\r
165   | L t -> L (aux (lev+1) t)\r
166   | A (t1, t2) -> A (aux lev t1, aux lev t2)\r
167   | C  | B as t -> t\r
168  in aux 0\r
169 ;;\r
170 let subst = subst 0 false;;\r
171 \r
172 let subst_in_problem ((v, t) as sub) p =\r
173 print_endline ("-- SUBST " ^ string_of_t (V v) ^ " |-> " ^ string_of_t t);\r
174  {p with\r
175   div=subst sub p.div;\r
176   conv=subst sub p.conv;\r
177   stepped=v::p.stepped;\r
178   sigma=sub::p.sigma}\r
179 ;;\r
180 \r
181 let get_subterms_with_head hd_var =\r
182  let rec aux lev inert_done = function\r
183  | C | V _ | B -> []\r
184  | L t -> aux (lev+1) false t\r
185  | A(t1,t2) as t ->\r
186    let hd_var', n_args' = get_inert t1 in\r
187    if not inert_done && hd_var' = V (hd_var + lev)\r
188     then lift ~-lev t :: aux lev true t1 @ aux lev false t2\r
189     else aux lev true t1 @ aux lev false t2\r
190  in aux 0 false\r
191 ;;\r
192 \r
193 let rec purify = function\r
194  | L t -> Pure.L (purify t)\r
195  | A (t1,t2) -> Pure.A (purify t1, purify t2)\r
196  | V n -> Pure.V n\r
197  | C -> Pure.V (min_int/2)\r
198  | B -> Pure.B\r
199 ;;\r
200 \r
201 let check p sigma =\r
202  print_endline "Checking...";\r
203  let div = purify p.div in\r
204  let conv = purify p.conv in\r
205  let sigma = List.map (fun (v,t) -> v, purify t) sigma in\r
206  let freshno = List.fold_right (max ++ fst) sigma 0 in\r
207  let env = Pure.env_of_sigma freshno sigma in\r
208  assert (Pure.diverged (Pure.mwhd (env,div,[])));\r
209  print_endline " D diverged.";\r
210  assert (not (Pure.diverged (Pure.mwhd (env,conv,[]))));\r
211  print_endline " C converged.";\r
212  ()\r
213 ;;\r
214 \r
215 let sanity p =\r
216  print_endline (string_of_problem p); (* non cancellare *)\r
217  if p.conv = B then problem_fail p "p.conv diverged";\r
218  if p.div = B then raise (Done p.sigma);\r
219  if p.phase = `Two && p.div = delta then raise (Done p.sigma);\r
220  if not (is_inert p.div) then problem_fail p "p.div converged";\r
221  (* Trailing constant args can be removed because do not contribute to eta-diff *)\r
222  let rec remove_trailing_constant_args = function\r
223  | A(t1, t2) when is_constant t2 -> remove_trailing_constant_args t1\r
224  | _ as t -> t in\r
225  let p = {p with div=remove_trailing_constant_args p.div} in\r
226  p\r
227 ;;\r
228 \r
229 (* drops the arguments of t after the n-th *)\r
230 let inert_cut_at n t =\r
231  let rec aux t =\r
232   match t with\r
233   | V _ as t -> 0, t\r
234   | A(t1,_) as t ->\r
235     let k', t' = aux t1 in\r
236      if k' = n then n, t'\r
237       else k'+1, t\r
238   | _ -> assert false\r
239  in snd (aux t)\r
240 ;;\r
241 \r
242 (* return the index of the first argument with a difference\r
243    (the first argument is 0) *)\r
244 let find_eta_difference p t =\r
245  let divargs = args_of_inert p.div in\r
246  let conargs = args_of_inert t in\r
247  let rec aux k divargs conargs =\r
248   match divargs,conargs with\r
249      [],_ -> []\r
250    | _::_,[] -> [k]\r
251    | t1::divargs,t2::conargs ->\r
252       (if not (eta_eq t1 t2) then [k] else []) @ aux (k+1) divargs conargs\r
253  in\r
254   aux 0 divargs conargs\r
255 ;;\r
256 \r
257 let compute_max_lambdas_at hd_var j =\r
258  let rec aux hd = function\r
259  | A(t1,t2) ->\r
260     (if get_inert t1 = (V hd, j)\r
261       then max ( (*FIXME*)\r
262        if is_inert t2 && let hd', j' = get_inert t2 in hd' = V hd\r
263         then let hd', j' = get_inert t2 in j - j'\r
264         else no_leading_lambdas hd_var j t2)\r
265       else id) (max (aux hd t1) (aux hd t2))\r
266  | L t -> aux (hd+1) t\r
267  | V _ | C -> 0\r
268  | _ -> assert false\r
269  in aux hd_var\r
270 ;;\r
271 \r
272 let print_cmd s1 s2 = print_endline (">> " ^ s1 ^ " " ^ s2);;\r
273 \r
274 (* returns Some i if i is the smallest integer s.t. p holds for the i-th\r
275    element of the list in input *)\r
276 let smallest_such_that p =\r
277  let rec aux i =\r
278   function\r
279      [] -> None\r
280    | hd::_ when (print_endline (string_of_t hd) ; p hd) -> Some i\r
281    | _::tl -> aux (i+1) tl\r
282  in\r
283   aux 0\r
284 ;;\r
285 \r
286 (* eat the arguments of the divergent and explode.\r
287  It does NOT perform any check, may fail if done unsafely *)\r
288 let eat p =\r
289 print_cmd "EAT" "";\r
290  let var, k = get_inert p.div in\r
291  match var with\r
292  | C | L _ | B | A _ -> assert false\r
293  | V var ->\r
294  let phase = p.phase in\r
295  let p =\r
296   match phase with\r
297   | `One ->\r
298       let i =\r
299        match smallest_such_that (fun x -> not (is_constant x)) (args_of_inert p.div) with\r
300           Some i -> i\r
301         | None -> assert false (*CSC: backtrack? *) in\r
302       let n = 1 + max\r
303        (compute_max_lambdas_at var (k-i-1) p.div)\r
304        (compute_max_lambdas_at var (k-i-1) p.conv) in\r
305       (* apply fresh vars *)\r
306       let p, t = fold_nat (fun (p, t) _ ->\r
307         let p, v = freshvar p in\r
308         p, A(t, V (v + k))\r
309       ) (p, V (k-1-i)) n in\r
310       let p = {p with phase=`Two} in\r
311       let t = A(t, delta) in\r
312       let t = fold_nat (fun t m -> if k-m = i then t else  A(t, V (k-m))) t k in\r
313       let subst = var, mk_lams t k in\r
314       let p = subst_in_problem subst p in\r
315       let _, args = get_inert p.div in\r
316       {p with div = inert_cut_at (args-k) p.div}\r
317   | `Two ->\r
318       let subst = var, mk_lams delta k in\r
319       subst_in_problem subst p in\r
320  sanity p\r
321 ;;\r
322 \r
323 (* step on the head of div, on the k-th argument, with n fresh vars *)\r
324 let step k n p =\r
325  let hd, _ = get_inert p.div in\r
326  match hd with\r
327  | C | L _ | B | A _  -> assert false\r
328  | V var ->\r
329 print_cmd "STEP" ("on " ^ string_of_t (V var) ^ " (on " ^ string_of_int (k+1) ^ "th)");\r
330  let p, t = (* apply fresh vars *)\r
331   fold_nat (fun (p, t) _ ->\r
332     let p, v = freshvar p in\r
333     p, A(t, V (v + k + 1))\r
334   ) (p, V 0) n in\r
335  let t = (* apply unused bound variables V_{k-1}..V_1 *)\r
336   fold_nat (fun t m -> A(t, V (k-m+1))) t k in\r
337  let t = mk_lams t (k+1) in (* make leading lambdas *)\r
338  let subst = var, t in\r
339  let p = subst_in_problem subst p in\r
340  sanity p\r
341 ;;\r
342 \r
343 let finish p =\r
344  (* one-step version of eat *)\r
345  let compute_max_arity =\r
346    let rec aux n = function\r
347    | A(t1,t2) -> max (aux (n+1) t1) (aux 0 t2)\r
348    | L t -> max n (aux 0 t)\r
349    | _ -> n\r
350  in aux 0 in\r
351 print_cmd "FINISH" "";\r
352  (* First, a step on the last argument of the divergent.\r
353     Because of the sanity check, it will never be a constant term. *)\r
354  let div_hd, div_nargs = get_inert p.div in\r
355  let div_hd = match div_hd with V n -> n | _ -> assert false in\r
356  let j = div_nargs - 1 in\r
357  let arity = compute_max_arity p.conv in\r
358  let n = 1 + arity + max\r
359   (compute_max_lambdas_at div_hd j p.div)\r
360   (compute_max_lambdas_at div_hd j p.conv) in\r
361  let p = step j n p in\r
362  (* Now, find first argument of div that is a variable never applied anywhere.\r
363  It must exist because of some invariant, since we just did a step,\r
364  and because of the arity of the divergent *)\r
365  let div_hd, div_nargs = get_inert p.div in\r
366  let div_hd = match div_hd with V n -> n | _ -> assert false in\r
367  let rec aux m = function\r
368  | A(t, V delta_var) ->\r
369    if delta_var <> div_hd && get_subterms_with_head delta_var p.conv = []\r
370    then m, delta_var\r
371    else aux (m-1) t\r
372  | A(t,_) -> aux (m-1) t\r
373  | _ -> assert false in\r
374  let m, delta_var = aux div_nargs p.div in\r
375  let p = subst_in_problem (delta_var, delta) p in\r
376  let p = subst_in_problem (div_hd, mk_lams delta (m-1)) p in\r
377  let p = {p with phase=`Two} in\r
378  sanity p\r
379 ;;\r
380 \r
381 let auto p =\r
382  let rec aux p =\r
383  let hd, n_args = get_inert p.div in\r
384  match hd with\r
385  | C | L _ | B | A _  -> assert false\r
386  | V hd_var ->\r
387  let tms = get_subterms_with_head hd_var p.conv in\r
388  if List.exists (fun t -> snd (get_inert t) >= n_args) tms\r
389   then (\r
390     (* let tms = List.sort (fun t1 t2 -> - compare (snd (get_inert t1)) (snd (get_inert t2))) tms in *)\r
391     List.iter (fun t -> try\r
392       let js = find_eta_difference p t in\r
393       (* print_endline (String.concat ", " (List.map string_of_int js)); *)\r
394       if js = [] then problem_fail p "no eta difference found (div subterm of conv?)";\r
395       let js = List.rev js in\r
396        List.iter\r
397         (fun j ->\r
398          try\r
399           let k = 1 + max\r
400            (compute_max_lambdas_at hd_var j p.div)\r
401             (compute_max_lambdas_at hd_var j p.conv) in\r
402           ignore (aux (step j k p))\r
403          with Fail(_, s) ->\r
404           print_endline ("Backtracking (eta_diff) because: " ^ s)) js;\r
405        raise (Fail(-1, "no eta difference"))\r
406       with Fail(_, s) ->\r
407        print_endline ("Backtracking (get_subterms) because: " ^ s)) tms;\r
408      raise (Fail(-1, "no similar terms"))\r
409    )\r
410   else\r
411     (*\r
412     (let phase = p.phase in\r
413      let p = eat p in\r
414      if phase = `Two\r
415       then problem_fail p "Auto.2 did not complete the problem"\r
416       else aux p)\r
417       *)\r
418     problem_fail (finish p) "Finish did not complete the problem"\r
419   in\r
420   try\r
421    aux p\r
422   with Done sigma -> sigma\r
423 ;;\r
424 \r
425 let problem_of (label, div, convs, ps, var_names) =\r
426  print_hline ();\r
427  let rec aux lev = function\r
428  | `Lam(_, t) -> L (aux (lev+1) t)\r
429  | `I (v, args) -> Listx.fold_left (fun x y -> mk_app x (aux lev y)) (aux lev (`Var v)) args\r
430  | `Var(v,_) -> if v >= lev && List.nth var_names (v-lev) = "C" then C else V v\r
431  | `N _ | `Match _ -> assert false in\r
432  assert (List.length ps = 0);\r
433  let convs = List.rev convs in\r
434  let conv = List.fold_left (fun x y -> mk_app x (aux 0 (y :> Num.nf))) (V (List.length var_names)) convs in\r
435  let var_names = "@" :: var_names in\r
436  let div = match div with\r
437  | Some div -> aux 0 (div :> Num.nf)\r
438  | None -> assert false in\r
439  let varno = List.length var_names in\r
440  {orig_freshno=varno; freshno=1+varno; div; conv; sigma=[]; stepped=[]; phase=`One}\r
441 ;;\r
442 \r
443 let solve p =\r
444  if is_constant p.div\r
445   then print_endline "!!! div is stuck. Problem was not run !!!"\r
446  else if eta_subterm p.div p.conv\r
447   then print_endline "!!! div is subterm of conv. Problem was not run !!!"\r
448   else let p = sanity p (* initial sanity check *) in check p (auto p)\r
449 ;;\r
450 \r
451 Problems.main (solve ++ problem_of);\r
452 \r
453 (* Example usage of interactive: *)\r
454 \r
455 (* let interactive div conv cmds =\r
456  let p = problem_of div conv in\r
457  try (\r
458  let p = List.fold_left (|>) p cmds in\r
459  let rec f p cmds =\r
460   let nth spl n = int_of_string (List.nth spl n) in\r
461   let read_cmd () =\r
462    let s = read_line () in\r
463    let spl = Str.split (Str.regexp " +") s in\r
464    s, let uno = List.hd spl in\r
465     try if uno = "eat" then eat\r
466     else if uno = "step" then step (nth spl 1) (nth spl 2)\r
467     else failwith "Wrong input."\r
468     with Failure s -> print_endline s; (fun x -> x) in\r
469   let str, cmd = read_cmd () in\r
470   let cmds = (" " ^ str ^ ";")::cmds in\r
471   try\r
472    let p = cmd p in f p cmds\r
473   with\r
474   | Done _ -> print_endline "Done! Commands history: "; List.iter print_endline (List.rev cmds)\r
475  in f p []\r
476  ) with Done _ -> ()\r
477 ;; *)\r
478 \r
479 (* interactive "x y"\r
480  "@ (x x) (y x) (y z)" [step 0 1; step 0 2; eat]\r
481 ;; *)\r