]> matita.cs.unibo.it Git - fireball-separation.git/blob - ocaml/simple.ml
New failing test
[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  p\r
222 ;;\r
223 \r
224 (* drops the arguments of t after the n-th *)\r
225 let inert_cut_at n t =\r
226  let rec aux t =\r
227   match t with\r
228   | V _ as t -> 0, t\r
229   | A(t1,_) as t ->\r
230     let k', t' = aux t1 in\r
231      if k' = n then n, t'\r
232       else k'+1, t\r
233   | _ -> assert false\r
234  in snd (aux t)\r
235 ;;\r
236 \r
237 (* return the index of the first argument with a difference\r
238    (the first argument is 0) *)\r
239 let find_eta_difference p t =\r
240  let divargs = args_of_inert p.div in\r
241  let conargs = args_of_inert t in\r
242  let rec aux k divargs conargs =\r
243   match divargs,conargs with\r
244      [],_ -> []\r
245    | _::_,[] -> [k]\r
246    | t1::divargs,t2::conargs ->\r
247       (if not (eta_eq t1 t2) then [k] else []) @ aux (k+1) divargs conargs\r
248  in\r
249   aux 0 divargs conargs\r
250 ;;\r
251 \r
252 let compute_max_lambdas_at hd_var j =\r
253  let rec aux hd = function\r
254  | A(t1,t2) ->\r
255     (if get_inert t1 = (V hd, j)\r
256       then max ( (*FIXME*)\r
257        if is_inert t2 && let hd', j' = get_inert t2 in hd' = V hd\r
258         then let hd', j' = get_inert t2 in j - j'\r
259         else no_leading_lambdas hd_var j t2)\r
260       else id) (max (aux hd t1) (aux hd t2))\r
261  | L t -> aux (hd+1) t\r
262  | V _ | C -> 0\r
263  | _ -> assert false\r
264  in aux hd_var\r
265 ;;\r
266 \r
267 let print_cmd s1 s2 = print_endline (">> " ^ s1 ^ " " ^ s2);;\r
268 \r
269 (* returns Some i if i is the smallest integer s.t. p holds for the i-th\r
270    element of the list in input *)\r
271 let smallest_such_that p =\r
272  let rec aux i =\r
273   function\r
274      [] -> None\r
275    | hd::_ when (print_endline (string_of_t hd) ; p hd) -> Some i\r
276    | _::tl -> aux (i+1) tl\r
277  in\r
278   aux 0\r
279 ;;\r
280 \r
281 (* eat the arguments of the divergent and explode.\r
282  It does NOT perform any check, may fail if done unsafely *)\r
283 let eat p =\r
284 print_cmd "EAT" "";\r
285  let var, k = get_inert p.div in\r
286  match var with\r
287  | C | L _ | B | A _ -> assert false\r
288  | V var ->\r
289  let phase = p.phase in\r
290  let p =\r
291   match phase with\r
292   | `One ->\r
293       let i =\r
294        match smallest_such_that (fun x -> not (is_constant x)) (args_of_inert p.div) with\r
295           Some i -> i\r
296         | None -> assert false (*CSC: backtrack? *) in\r
297       let n = 1 + max\r
298        (compute_max_lambdas_at var (k-i-1) p.div)\r
299        (compute_max_lambdas_at var (k-i-1) p.conv) in\r
300       (* apply fresh vars *)\r
301       let p, t = fold_nat (fun (p, t) _ ->\r
302         let p, v = freshvar p in\r
303         p, A(t, V (v + k))\r
304       ) (p, V (k-1-i)) n in\r
305       let p = {p with phase=`Two} in\r
306       let t = A(t, delta) in\r
307       let t = fold_nat (fun t m -> if k-m = i then t else  A(t, V (k-m))) t k in\r
308       let subst = var, mk_lams t k in\r
309       let p = subst_in_problem subst p in\r
310       let _, args = get_inert p.div in\r
311       {p with div = inert_cut_at (args-k) p.div}\r
312   | `Two ->\r
313       let subst = var, mk_lams delta k in\r
314       subst_in_problem subst p in\r
315  sanity p\r
316 ;;\r
317 \r
318 (* step on the head of div, on the k-th argument, with n fresh vars *)\r
319 let step k n p =\r
320  let hd, _ = get_inert p.div in\r
321  match hd with\r
322  | C | L _ | B | A _  -> assert false\r
323  | V var ->\r
324 print_cmd "STEP" ("on " ^ string_of_t (V var) ^ " (on " ^ string_of_int (k+1) ^ "th)");\r
325  let p, t = (* apply fresh vars *)\r
326   fold_nat (fun (p, t) _ ->\r
327     let p, v = freshvar p in\r
328     p, A(t, V (v + k + 1))\r
329   ) (p, V 0) n in\r
330  let t = (* apply unused bound variables V_{k-1}..V_1 *)\r
331   fold_nat (fun t m -> A(t, V (k-m+1))) t k in\r
332  let t = mk_lams t (k+1) in (* make leading lambdas *)\r
333  let subst = var, t in\r
334  let p = subst_in_problem subst p in\r
335  sanity p\r
336 ;;\r
337 \r
338 let auto p =\r
339  let rec aux p =\r
340  let hd, n_args = get_inert p.div in\r
341  match hd with\r
342  | C | L _ | B | A _  -> assert false\r
343  | V hd_var ->\r
344  let tms = get_subterms_with_head hd_var p.conv in\r
345  if List.exists (fun t -> snd (get_inert t) >= n_args) tms\r
346   then (\r
347     (* let tms = List.sort (fun t1 t2 -> - compare (snd (get_inert t1)) (snd (get_inert t2))) tms in *)\r
348     List.iter (fun t -> try\r
349       let js = find_eta_difference p t in\r
350       (* print_endline (String.concat ", " (List.map string_of_int js)); *)\r
351       if js = [] then problem_fail p "no eta difference found (div subterm of conv?)";\r
352       let js = List.rev js in\r
353        List.iter\r
354         (fun j ->\r
355          try\r
356           let k = 1 + max\r
357            (compute_max_lambdas_at hd_var j p.div)\r
358             (compute_max_lambdas_at hd_var j p.conv) in\r
359           ignore (aux (step j k p))\r
360          with Fail(_, s) ->\r
361           print_endline ("Backtracking (eta_diff) because: " ^ s)) js;\r
362        raise (Fail(-1, "no eta difference"))\r
363       with Fail(_, s) ->\r
364        print_endline ("Backtracking (get_subterms) because: " ^ s)) tms;\r
365      raise (Fail(-1, "no similar terms"))\r
366    )\r
367   else\r
368     (let phase = p.phase in\r
369      let p = eat p in\r
370      if phase = `Two\r
371       then problem_fail p "Auto.2 did not complete the problem"\r
372       else aux p)\r
373   in\r
374   try\r
375    aux p\r
376   with Done sigma -> sigma\r
377 ;;\r
378 \r
379 let problem_of (label, div, convs, ps, var_names) =\r
380  print_hline ();\r
381  let rec aux lev = function\r
382  | `Lam(_, t) -> L (aux (lev+1) t)\r
383  | `I (v, args) -> Listx.fold_left (fun x y -> mk_app x (aux lev y)) (aux lev (`Var v)) args\r
384  | `Var(v,_) -> if v >= lev && List.nth var_names (v-lev) = "C" then C else V v\r
385  | `N _ | `Match _ -> assert false in\r
386  assert (List.length ps = 0);\r
387  let convs = List.rev convs in\r
388  let conv = List.fold_left (fun x y -> mk_app x (aux 0 (y :> Num.nf))) (V (List.length var_names)) convs in\r
389  let var_names = "@" :: var_names in\r
390  let div = match div with\r
391  | Some div -> aux 0 (div :> Num.nf)\r
392  | None -> assert false in\r
393  let varno = List.length var_names in\r
394  let p = {orig_freshno=varno; freshno=1+varno; div; conv; sigma=[]; stepped=[]; phase=`One} in\r
395  (* initial sanity check *)\r
396  sanity p\r
397 ;;\r
398 \r
399 let solve p =\r
400  if is_stuck p.div then print_endline "!!! div is stuck. Problem was not run !!!"\r
401  else if eta_subterm p.div p.conv\r
402   then print_endline "!!! div is subterm of conv. Problem was not run !!!"\r
403   else check p (auto p)\r
404 ;;\r
405 \r
406 Problems.main (solve ++ problem_of);\r
407 \r
408 (* Example usage of interactive: *)\r
409 \r
410 (* let interactive div conv cmds =\r
411  let p = problem_of div conv in\r
412  try (\r
413  let p = List.fold_left (|>) p cmds in\r
414  let rec f p cmds =\r
415   let nth spl n = int_of_string (List.nth spl n) in\r
416   let read_cmd () =\r
417    let s = read_line () in\r
418    let spl = Str.split (Str.regexp " +") s in\r
419    s, let uno = List.hd spl in\r
420     try if uno = "eat" then eat\r
421     else if uno = "step" then step (nth spl 1) (nth spl 2)\r
422     else failwith "Wrong input."\r
423     with Failure s -> print_endline s; (fun x -> x) in\r
424   let str, cmd = read_cmd () in\r
425   let cmds = (" " ^ str ^ ";")::cmds in\r
426   try\r
427    let p = cmd p in f p cmds\r
428   with\r
429   | Done _ -> print_endline "Done! Commands history: "; List.iter print_endline (List.rev cmds)\r
430  in f p []\r
431  ) with Done _ -> ()\r
432 ;; *)\r
433 \r
434 (* interactive "x y"\r
435  "@ (x x) (y x) (y z)" [step 0 1; step 0 2; eat]\r
436 ;; *)\r