]> matita.cs.unibo.it Git - helm.git/blob - helm/software/components/ng_tactics/nAuto.ml
7a33d6266da45052fc2a16328d60f8dcbf5ce5b8
[helm.git] / helm / software / components / ng_tactics / nAuto.ml
1 (*
2     ||M||  This file is part of HELM, an Hypertextual, Electronic        
3     ||A||  Library of Mathematics, developed at the Computer Science     
4     ||T||  Department, University of Bologna, Italy.                     
5     ||I||                                                                
6     ||T||  HELM is free software; you can redistribute it and/or         
7     ||A||  modify it under the terms of the GNU General Public License   
8     \   /  version 2 or (at your option) any later version.      
9      \ /   This software is distributed as is, NO WARRANTY.     
10       V_______________________________________________________________ *)
11
12 (* $Id: nCic.ml 9058 2008-10-13 17:42:30Z tassi $ *)
13
14 open Printf
15
16 let debug = ref true
17 let debug_print ?(depth=0) s = 
18   if !debug then prerr_endline (String.make depth '\t'^Lazy.force s) else ()
19 let debug_do f = if !debug then f () else ()
20
21 open Continuationals.Stack
22 open NTacStatus
23 module Ast = CicNotationPt
24
25 (* =================================== paramod =========================== *)
26 let auto_paramod ~params:(l,_) status goal =
27   let l = match l with
28     | None -> raise (Error (lazy "no proof found",None))
29     | Some l -> l in
30   let gty = get_goalty status goal in
31   let n,h,metasenv,subst,o = status#obj in
32   let status,t = term_of_cic_term status gty (ctx_of gty) in
33   let status, l = 
34     List.fold_left
35       (fun (status, l) t ->
36         let status, t = disambiguate status (ctx_of gty) t None in
37         let status, ty = typeof status (ctx_of t) t in
38         let status, t =  term_of_cic_term status t (ctx_of gty) in
39         let status, ty = term_of_cic_term status ty (ctx_of ty) in
40         (status, (t,ty) :: l))
41       (status,[]) l
42   in
43   match
44     NCicParamod.nparamod status metasenv subst (ctx_of gty) (NCic.Rel ~-1,t) l 
45   with
46   | [] -> raise (Error (lazy "no proof found",None))
47   | (pt, _, metasenv, subst)::_ -> 
48       let status = status#set_obj (n,h,metasenv,subst,o) in
49       instantiate status goal (mk_cic_term (ctx_of gty) pt)
50 ;;
51
52 let auto_paramod_tac ~params status = 
53   NTactics.distribute_tac (auto_paramod ~params) status
54 ;;
55
56 (* =================================== auto =========================== *)
57 (****************** AUTO ********************
58
59 let calculate_timeout flags = 
60     if flags.timeout = 0. then 
61       (debug_print (lazy "AUTO WITH NO TIMEOUT");
62        {flags with timeout = infinity})
63     else 
64       flags 
65 ;;
66 let is_equational_case goalty flags =
67   let ensure_equational t = 
68     if is_an_equational_goal t then true 
69     else false
70   in
71   (flags.use_paramod && is_an_equational_goal goalty) || 
72   (flags.use_only_paramod && ensure_equational goalty)
73 ;;
74
75 type menv = Cic.metasenv
76 type subst = Cic.substitution
77 type goal = ProofEngineTypes.goal * int * AutoTypes.sort
78 let candidate_no = ref 0;;
79 type candidate = int * Cic.term Lazy.t
80 type cache = AutoCache.cache
81
82 type fail = 
83   (* the goal (mainly for depth) and key of the goal *)
84   goal * AutoCache.cache_key
85 type op = 
86   (* goal has to be proved *)
87   | D of goal 
88   (* goal has to be cached as a success obtained using candidate as the first
89    * step *)
90   | S of goal * AutoCache.cache_key * candidate * int 
91 type elem = 
92   (* menv, subst, size, operations done (only S), operations to do, failures to cache if any op fails *)
93   menv * subst * int * op list * op list * fail list 
94 type status = 
95   (* list of computations that may lead to the solution: all op list will
96    * end with the same (S(g,_)) *)
97   elem list
98 type auto_result = 
99   (* menv, subst, alternatives, tables, cache *)
100   | Proved of menv * subst * elem list * AutomationCache.tables * cache 
101   | Gaveup of AutomationCache.tables * cache 
102
103
104 (* the status exported to the external observer *)  
105 type auto_status = 
106   (* context, (goal,candidate) list, and_list, history *)
107   Cic.context * (int * Cic.term * bool * int * (int * Cic.term Lazy.t) list) list * 
108   (int * Cic.term * int) list * Cic.term Lazy.t list
109
110 let d_prefix l =
111   let rec aux acc = function
112     | (D g)::tl -> aux (acc@[g]) tl
113     | _ -> acc
114   in
115     aux [] l
116 ;;
117
118 let calculate_goal_ty (goalno,_,_) s m = 
119   try
120     let _,cc,goalty = CicUtil.lookup_meta goalno m in
121     (* XXX applicare la subst al contesto? *)
122     Some (cc, CicMetaSubst.apply_subst s goalty)
123   with CicUtil.Meta_not_found i when i = goalno -> None
124 ;;
125
126 let calculate_closed_goal_ty (goalno,_,_) s = 
127   try
128     let cc,_,goalty = List.assoc goalno s in
129     (* XXX applicare la subst al contesto? *)
130     Some (cc, CicMetaSubst.apply_subst s goalty)
131   with Not_found -> 
132     None
133 ;;
134
135 let pp_status ctx status = 
136   if debug then 
137   let names = Utils.names_of_context ctx in
138   let pp x = 
139     let x = 
140       ProofEngineReduction.replace 
141         ~equality:(fun a b -> match b with Cic.Meta _ -> true | _ -> false) 
142           ~what:[Cic.Rel 1] ~with_what:[Cic.Implicit None] ~where:x
143     in
144     CicPp.pp x names
145   in
146   let string_of_do m s (gi,_,_ as g) d =
147     match calculate_goal_ty g s m with
148     | Some (_,gty) -> Printf.sprintf "D(%d, %s, %d)" gi (pp gty) d
149     | None -> Printf.sprintf "D(%d, _, %d)" gi d
150   in
151   let string_of_s m su k (ci,ct) gi =
152     Printf.sprintf "S(%d, %s, %s, %d)" gi (pp k) (pp (Lazy.force ct)) ci
153   in
154   let string_of_ol m su l =
155     String.concat " | " 
156       (List.map 
157         (function 
158           | D (g,d,s) -> string_of_do m su (g,d,s) d 
159           | S ((gi,_,_),k,c,_) -> string_of_s m su k c gi) 
160         l)
161   in
162   let string_of_fl m s fl = 
163     String.concat " | " 
164       (List.map (fun ((i,_,_),ty) -> 
165          Printf.sprintf "(%d, %s)" i (pp ty)) fl)
166   in
167   let rec aux = function
168     | [] -> ()
169     | (m,s,_,_,ol,fl)::tl ->
170         Printf.eprintf "< [%s] ;;; [%s]>\n" 
171           (string_of_ol m s ol) (string_of_fl m s fl);
172         aux tl
173   in
174     Printf.eprintf "-------------------------- status -------------------\n";
175     aux status;
176     Printf.eprintf "-----------------------------------------------------\n";
177 ;;
178   
179 let auto_status = ref [] ;;
180 let auto_context = ref [];;
181 let in_pause = ref false;;
182 let pause b = in_pause := b;;
183 let cond = Condition.create ();;
184 let mutex = Mutex.create ();;
185 let hint = ref None;;
186 let prune_hint = ref [];;
187
188 let step _ = Condition.signal cond;;
189 let give_hint n = hint := Some n;;
190 let give_prune_hint hint =
191   prune_hint := hint :: !prune_hint
192 ;;
193
194 let check_pause _ =
195   if !in_pause then
196     begin
197       Mutex.lock mutex;
198       Condition.wait cond mutex;
199       Mutex.unlock mutex
200     end
201 ;;
202
203 let get_auto_status _ = 
204   let status = !auto_status in
205   let and_list,elems,last = 
206     match status with
207     | [] -> [],[],[]
208     | (m,s,_,don,gl,fail)::tl ->
209         let and_list = 
210           HExtlib.filter_map 
211             (fun (id,d,_ as g) -> 
212               match calculate_goal_ty g s m with
213               | Some (_,x) -> Some (id,x,d) | None -> None)
214             (d_goals gl)
215         in
216         let rows = 
217           (* these are the S goalsin the or list *)
218           let orlist = 
219             List.map
220               (fun (m,s,_,don,gl,fail) -> 
221                 HExtlib.filter_map
222                   (function S (g,k,c,_) -> Some (g,k,c) | _ -> None) 
223                   (List.rev don @ gl))
224               status
225           in
226           (* this function eats id from a list l::[id,x] returning x, l *)
227           let eat_tail_if_eq id l = 
228             let rec aux (s, l) = function
229               | [] -> s, l
230               | ((id1,_,_),k1,c)::tl when id = id1 ->
231                   (match s with
232                   | None -> aux (Some c,l) tl
233                   | Some _ -> assert false)
234               | ((id1,_,_),k1,c as e)::tl -> aux (s, e::l) tl
235             in
236             let c, l = aux (None, []) l in
237             c, List.rev l
238           in
239           let eat_in_parallel id l =
240             let rec aux (b,eaten, new_l as acc) l =
241               match l with
242               | [] -> acc
243               | l::tl ->
244                   match eat_tail_if_eq id l with
245                   | None, l -> aux (b@[false], eaten, new_l@[l]) tl
246                   | Some t,l -> aux (b@[true],eaten@[t], new_l@[l]) tl
247             in
248             aux ([],[],[]) l
249           in
250           let rec eat_all rows l =
251             match l with
252             | [] -> rows
253             | elem::or_list ->
254                 match List.rev elem with
255                 | ((to_eat,depth,_),k,_)::next_lunch ->
256                     let b, eaten, l = eat_in_parallel to_eat l in
257                     let eaten = HExtlib.list_uniq eaten in
258                     let eaten = List.rev eaten in
259                     let b = true (* List.hd (List.rev b) *) in
260                     let rows = rows @ [to_eat,k,b,depth,eaten] in
261                     eat_all rows l
262                 | [] -> eat_all rows or_list
263           in
264           eat_all [] (List.rev orlist)
265         in
266         let history = 
267           HExtlib.filter_map
268             (function (S (_,_,(_,c),_)) -> Some c | _ -> None) 
269             gl 
270         in
271 (*         let rows = List.filter (fun (_,l) -> l <> []) rows in *)
272         and_list, rows, history
273   in
274   !auto_context, elems, and_list, last
275 ;;
276
277 (* Works if there is no dependency over proofs *)
278 let is_a_green_cut goalty =
279   CicUtil.is_meta_closed goalty
280 ;;
281 let rec first_s = function
282   | (D _)::tl -> first_s tl
283   | (S (g,k,c,s))::tl -> Some ((g,k,c,s),tl)
284   | [] -> None
285 ;;
286 let list_union l1 l2 =
287   (* TODO ottimizzare compare *)
288   HExtlib.list_uniq (List.sort compare (l1 @ l1))
289 ;;
290 let rec eq_todo l1 l2 =
291   match l1,l2 with
292   | (D g1) :: tl1,(D g2) :: tl2 when g1=g2 -> eq_todo tl1 tl2
293   | (S (g1,k1,(c1,lt1),i1)) :: tl1, (S (g2,k2,(c2,lt2),i2)) :: tl2
294     when i1 = i2 && g1 = g2 && k1 = k2 && c1 = c2 ->
295       if Lazy.force lt1 = Lazy.force lt2 then eq_todo tl1 tl2 else false
296   | [],[] -> true
297   | _ -> false
298 ;;
299 let eat_head todo id fl orlist = 
300   let rec aux acc = function
301   | [] -> [], acc
302   | (m, s, _, _, todo1, fl1)::tl as orlist -> 
303       let rec aux1 todo1 =
304         match first_s todo1 with
305         | None -> orlist, acc
306         | Some (((gno,_,_),_,_,_), todo11) ->
307             (* TODO confronto tra todo da ottimizzare *)
308             if gno = id && eq_todo todo11 todo then 
309               aux (list_union fl1 acc) tl
310             else 
311               aux1 todo11
312       in
313        aux1 todo1
314   in 
315     aux fl orlist
316 ;;
317 let close_proof p ty menv context = 
318   let metas =
319     List.map fst (CicUtil.metas_of_term p @ CicUtil.metas_of_term ty)
320   in
321   let menv = List.filter (fun (i,_,_) -> List.exists ((=)i) metas) menv in
322   naif_closure p menv context
323 ;;
324 (* XXX capire bene quando aggiungere alla cache *)
325 let add_to_cache_and_del_from_orlist_if_green_cut
326   g s m cache key todo orlist fl ctx size minsize
327
328   let cache = cache_remove_underinspection cache key in
329   (* prima per fare la irl usavamo il contesto vero e proprio e non quello 
330    * canonico! XXX *)
331   match calculate_closed_goal_ty g s with
332   | None -> assert false
333   | Some (canonical_ctx , gty) ->
334       let goalno,depth,sort = g in
335       let irl = mk_irl canonical_ctx in
336       let goal = Cic.Meta(goalno, irl) in
337       let proof = CicMetaSubst.apply_subst s goal in
338       let green_proof, closed_proof = 
339         let b = is_a_green_cut proof in
340         if not b then
341           b, (* close_proof proof gty m ctx *) proof 
342         else
343           b, proof
344       in
345       debug_print (lazy ("TENTATIVE CACHE: " ^ CicPp.ppterm key));
346       if is_a_green_cut key then
347         (* if the initia goal was closed, we cut alternatives *)
348         let _ = debug_print (lazy ("MANGIO: " ^ string_of_int goalno)) in
349         let orlist, fl = eat_head todo goalno fl orlist in
350         let cache = 
351           if size < minsize then 
352             (debug_print (lazy ("NO CACHE: 2 (size <= minsize)"));cache)
353           else 
354           (* if the proof is closed we cache it *)
355           if green_proof then cache_add_success cache key proof
356           else (* cache_add_success cache key closed_proof *) 
357             (debug_print (lazy ("NO CACHE: (no gree proof)"));cache)
358         in
359         cache, orlist, fl, true
360       else
361         let cache = 
362           debug_print (lazy ("TENTATIVE CACHE: " ^ CicPp.ppterm gty));
363           if size < minsize then 
364             (debug_print (lazy ("NO CACHE: (size <= minsize)")); cache) else
365           (* if the substituted goal and the proof are closed we cache it *)
366           if is_a_green_cut gty then
367             if green_proof then cache_add_success cache gty proof
368             else (* cache_add_success cache gty closed_proof *) 
369               (debug_print (lazy ("NO CACHE: (no green proof (gty))"));cache)
370           else (*
371             try
372               let ty, _ =
373                 CicTypeChecker.type_of_aux' ~subst:s 
374                   m ctx closed_proof CicUniv.oblivion_ugraph
375               in
376               if is_a_green_cut ty then 
377                 cache_add_success cache ty closed_proof
378               else cache
379             with
380             | CicTypeChecker.TypeCheckerFailure _ ->*) 
381           (debug_print (lazy ("NO CACHE: (no green gty )"));cache)
382         in
383         cache, orlist, fl, false
384 ;;
385 let close_failures (fl : fail list) (cache : cache) = 
386   List.fold_left 
387     (fun cache ((gno,depth,_),gty) -> 
388       if CicUtil.is_meta_closed gty then
389        ( debug_print (lazy ("FAIL: INDUCED: " ^ string_of_int gno));
390          cache_add_failure cache gty depth) 
391       else
392          cache)
393     cache fl
394 ;;
395 let put_in_subst subst metasenv  (goalno,_,_) canonical_ctx t ty =
396   let entry = goalno, (canonical_ctx, t,ty) in
397   assert_subst_are_disjoint subst [entry];
398   let subst = entry :: subst in
399   
400   let metasenv = CicMetaSubst.apply_subst_metasenv subst metasenv in
401
402   subst, metasenv
403 ;;
404
405 let mk_fake_proof metasenv subst (goalno,_,_) goalty context = 
406   None,metasenv,subst ,(lazy (Cic.Meta(goalno,mk_irl context))),goalty, [] 
407 ;;
408
409 let equational_case 
410   tables cache depth fake_proof goalno goalty subst context 
411     flags
412 =
413   let active,passive,bag = tables in
414   let ppterm = ppterm context in
415   let status = (fake_proof,goalno) in
416     if flags.use_only_paramod then
417       begin
418         debug_print (lazy ("PARAMODULATION SU: " ^ 
419                          string_of_int goalno ^ " " ^ ppterm goalty ));
420         let goal_steps, saturation_steps, timeout =
421           max_int,max_int,flags.timeout 
422         in
423         match
424           Saturation.given_clause bag status active passive 
425             goal_steps saturation_steps timeout
426         with 
427           | None, active, passive, bag -> 
428               [], (active,passive,bag), cache, flags
429           | Some(subst',(_,metasenv,_subst,proof,_, _),open_goals),active,
430             passive,bag ->
431               assert_subst_are_disjoint subst subst';
432               let subst = subst@subst' in
433               let open_goals = 
434                 order_new_goals metasenv subst open_goals ppterm 
435               in
436               let open_goals = 
437                 List.map (fun (x,sort) -> x,depth-1,sort) open_goals 
438               in
439               incr candidate_no;
440               [(!candidate_no,proof),metasenv,subst,open_goals], 
441                 (active,passive,bag), cache, flags
442       end
443     else
444       begin
445         debug_print (lazy ("NARROWING DEL GOAL: " ^ 
446                          string_of_int goalno ^ " " ^ ppterm goalty ));
447         let goal_steps, saturation_steps, timeout =
448           1,0,flags.timeout 
449         in
450         match
451           Saturation.solve_narrowing bag status active passive goal_steps 
452         with 
453           | None, active, passive, bag -> 
454               [], (active,passive,bag), cache, flags
455           | Some(subst',(_,metasenv,_subst,proof,_, _),open_goals),active,
456             passive,bag ->
457               assert_subst_are_disjoint subst subst';
458               let subst = subst@subst' in
459               let open_goals = 
460                 order_new_goals metasenv subst open_goals ppterm 
461               in
462               let open_goals = 
463                 List.map (fun (x,sort) -> x,depth-1,sort) open_goals 
464               in
465               incr candidate_no;
466               [(!candidate_no,proof),metasenv,subst,open_goals], 
467                 (active,passive,bag), cache, flags
468       end
469 (*
470       begin
471         let params = ([],["use_context","false"]) in
472         let automation_cache = { 
473               AutomationCache.tables = tables ;
474               AutomationCache.univ = Universe.empty; }
475         in
476         try 
477           let ((_,metasenv,subst,_,_,_),open_goals) =
478
479             solve_rewrite ~params ~automation_cache
480               (fake_proof, goalno)
481           in
482           let proof = lazy (Cic.Meta (-1,[])) in
483           [(!candidate_no,proof),metasenv,subst,[]],tables, cache, flags
484         with ProofEngineTypes.Fail _ -> [], tables, cache, flags
485 (*
486         let res = Saturation.all_subsumed bag status active passive in
487         let res' =
488           List.map 
489             (fun (subst',(_,metasenv,_subst,proof,_, _),open_goals) ->
490                assert_subst_are_disjoint subst subst';
491                let subst = subst@subst' in
492                let open_goals = 
493                  order_new_goals metasenv subst open_goals ppterm 
494                in
495                let open_goals = 
496                  List.map (fun (x,sort) -> x,depth-1,sort) open_goals 
497                in
498                incr candidate_no;
499                  (!candidate_no,proof),metasenv,subst,open_goals)
500             res 
501           in
502           res', (active,passive,bag), cache, flags 
503 *)
504       end
505 *)
506 ;;
507
508 let sort_new_elems = 
509  List.sort (fun (_,_,_,l1) (_,_,_,l2) -> 
510          let p1 = List.length (prop_only l1) in 
511          let p2 = List.length (prop_only l2) in
512          if p1 = p2 then List.length l1 - List.length l2 else p1-p2)
513 ;;
514
515
516 let try_candidate dbd
517   goalty tables subst fake_proof goalno depth context cand 
518 =
519   let ppterm = ppterm context in
520   try 
521     let actives, passives, bag = tables in 
522     let (_,metasenv,subst,_,_,_), open_goals =
523        ProofEngineTypes.apply_tactic
524         (PrimitiveTactics.apply_tac ~term:cand)
525         (fake_proof,goalno) 
526     in
527     let tables = actives, passives, 
528       Equality.push_maxmeta bag 
529         (max (Equality.maxmeta bag) (CicMkImplicit.new_meta metasenv subst)) 
530     in
531     debug_print (lazy ("   OK: " ^ ppterm cand));
532     let metasenv = CicRefine.pack_coercion_metasenv metasenv in
533     let open_goals = order_new_goals metasenv subst open_goals ppterm in
534     let open_goals = List.map (fun (x,sort) -> x,depth-1,sort) open_goals in
535     incr candidate_no;
536     Some ((!candidate_no,lazy cand),metasenv,subst,open_goals), tables 
537   with 
538     | ProofEngineTypes.Fail s -> None,tables
539     | CicUnification.Uncertain s ->  None,tables
540 ;;
541
542 let applicative_case dbd
543   tables depth subst fake_proof goalno goalty metasenv context 
544   signature universe cache flags
545
546   (* let goalty_aux = 
547     match goalty with
548     | Cic.Appl (hd::tl) -> 
549         Cic.Appl (hd :: HExtlib.mk_list (Cic.Meta (0,[])) (List.length tl))
550     | _ -> goalty
551   in *)
552   let goalty_aux = goalty in
553   let candidates = 
554     get_candidates flags.skip_trie_filtering universe cache goalty_aux
555   in
556   (* if the goal is an equality we skip the congruence theorems 
557   let candidates =
558     if is_equational_case goalty flags 
559     then List.filter not_default_eq_term candidates 
560     else candidates 
561   in *)
562   let candidates = List.filter (only signature context metasenv) candidates 
563   in
564   let tables, elems = 
565     List.fold_left 
566       (fun (tables,elems) cand ->
567         match 
568           try_candidate dbd goalty
569             tables subst fake_proof goalno depth context cand
570         with
571         | None, tables -> tables, elems
572         | Some x, tables -> tables, x::elems)
573       (tables,[]) candidates
574   in
575   let elems = sort_new_elems elems in
576   elems, tables, cache
577 ;;
578
579 let try_smart_candidate dbd
580   goalty tables subst fake_proof goalno depth context cand 
581 =
582   let ppterm = ppterm context in
583   try
584     let params = ([],[]) in
585     let automation_cache = { 
586           AutomationCache.tables = tables ;
587           AutomationCache.univ = Universe.empty; }
588     in
589     debug_print (lazy ("candidato per " ^ string_of_int goalno 
590       ^ ": " ^ CicPp.ppterm cand));
591 (*
592     let (_,metasenv,subst,_,_,_) = fake_proof in
593     prerr_endline ("metasenv:\n" ^ CicMetaSubst.ppmetasenv [] metasenv);
594     prerr_endline ("subst:\n" ^ CicMetaSubst.ppsubst ~metasenv subst);
595 *)
596     let ((_,metasenv,subst,_,_,_),open_goals) =
597       apply_smart ~dbd ~term:cand ~params ~automation_cache
598         (fake_proof, goalno)
599     in
600     let metasenv = CicRefine.pack_coercion_metasenv metasenv in
601     let open_goals = order_new_goals metasenv subst open_goals ppterm in
602     let open_goals = List.map (fun (x,sort) -> x,depth-1,sort) open_goals in
603     incr candidate_no;
604     Some ((!candidate_no,lazy cand),metasenv,subst,open_goals), tables 
605   with 
606   | ProofEngineTypes.Fail s -> None,tables
607   | CicUnification.Uncertain s ->  None,tables
608 ;;
609
610 let smart_applicative_case dbd
611   tables depth subst fake_proof goalno goalty metasenv context signature
612   universe cache flags
613
614   let goalty_aux = 
615     match goalty with
616     | Cic.Appl (hd::tl) -> 
617         Cic.Appl (hd :: HExtlib.mk_list (Cic.Meta (0,[])) (List.length tl))
618     | _ -> goalty
619   in
620   let smart_candidates = 
621     get_candidates flags.skip_trie_filtering universe cache goalty_aux
622   in
623   let candidates = 
624     get_candidates flags.skip_trie_filtering universe cache goalty
625   in
626   let smart_candidates = 
627     List.filter
628       (fun x -> not(List.mem x candidates)) smart_candidates
629   in 
630   let debug_msg =
631     (lazy ("smart_candidates" ^ " = " ^ 
632              (String.concat "\n" (List.map CicPp.ppterm smart_candidates)))) in
633   debug_print debug_msg;
634   let candidates = List.filter (only signature context metasenv) candidates in
635   let smart_candidates = 
636     List.filter (only signature context metasenv) smart_candidates 
637   in
638 (*
639   let penalty cand depth = 
640     if only signature context metasenv cand then depth else ((prerr_endline (
641     "penalizzo " ^ CicPp.ppterm cand));depth -1)
642   in
643 *)
644   let tables, elems = 
645     List.fold_left 
646       (fun (tables,elems) cand ->
647         match 
648           try_candidate dbd goalty
649             tables subst fake_proof goalno depth context cand
650         with
651         | None, tables ->
652             (* if normal application fails we try to be smart *)
653             (match try_smart_candidate dbd goalty
654                tables subst fake_proof goalno depth context cand
655              with
656                | None, tables -> tables, elems
657                | Some x, tables -> tables, x::elems)
658         | Some x, tables -> tables, x::elems)
659       (tables,[]) candidates
660   in
661   let tables, smart_elems = 
662       List.fold_left 
663         (fun (tables,elems) cand ->
664           match 
665             try_smart_candidate dbd goalty
666               tables subst fake_proof goalno depth context cand
667           with
668           | None, tables -> tables, elems
669           | Some x, tables -> tables, x::elems)
670         (tables,[]) smart_candidates
671   in
672   let elems = sort_new_elems (elems @ smart_elems) in
673   elems, tables, cache
674 ;;
675
676 let equational_and_applicative_case dbd
677   signature universe flags m s g gty tables cache context 
678 =
679   let goalno, depth, sort = g in
680   let fake_proof = mk_fake_proof m s g gty context in
681   if is_equational_case gty flags then
682     let elems,tables,cache, flags =
683       equational_case tables cache
684         depth fake_proof goalno gty s context flags 
685     in
686     let more_elems, tables, cache =
687       if flags.use_only_paramod then
688         [],tables, cache
689       else
690         applicative_case dbd
691           tables depth s fake_proof goalno 
692             gty m context signature universe cache flags
693     in
694       elems@more_elems, tables, cache, flags            
695   else
696     let elems, tables, cache =
697       match LibraryObjects.eq_URI () with
698       | Some _ ->
699          smart_applicative_case dbd tables depth s fake_proof goalno 
700            gty m context signature universe cache flags
701       | None -> 
702          applicative_case dbd tables depth s fake_proof goalno 
703            gty m context signature universe cache flags
704     in
705       elems, tables, cache, flags  
706 ;;
707 let rec condition_for_hint i = function
708   | [] -> false
709   | S (_,_,(j,_),_):: tl -> j <> i (* && condition_for_hint i tl *)
710   | _::tl -> condition_for_hint i tl
711 ;;
712 let prunable_for_size flags s m todo =
713   let rec aux b = function
714     | (S _)::tl -> aux b tl
715     | (D (_,_,T))::tl -> aux b tl
716     | (D g)::tl -> 
717         (match calculate_goal_ty g s m with
718           | None -> aux b tl
719           | Some (canonical_ctx, gty) -> 
720             let gsize, _ = 
721               Utils.weight_of_term 
722                 ~consider_metas:false ~count_metas_occurrences:true gty in
723             let newb = b || gsize > flags.maxgoalsizefactor in
724             aux newb tl)
725     | [] -> b
726   in
727     aux false todo
728
729 (*
730 let prunable ty todo =
731   let rec aux b = function
732     | (S(_,k,_,_))::tl -> aux (b || Equality.meta_convertibility k ty) tl
733     | (D (_,_,T))::tl -> aux b tl
734     | D _::_ -> false
735     | [] -> b
736   in
737     aux false todo
738 ;;
739 *)
740
741 let prunable menv subst ty todo =
742   let rec aux = function
743     | (S(_,k,_,_))::tl ->
744          (match Equality.meta_convertibility_subst k ty menv with
745           | None -> aux tl
746           | Some variant -> 
747                no_progress variant tl (* || aux tl*))
748     | (D (_,_,T))::tl -> aux tl
749     | _ -> false
750   and no_progress variant = function
751     | [] -> (*prerr_endline "++++++++++++++++++++++++ no_progress";*) true
752     | D ((n,_,P) as g)::tl -> 
753         (match calculate_goal_ty g subst menv with
754            | None -> no_progress variant tl
755            | Some (_, gty) -> 
756                (match calculate_goal_ty g variant menv with
757                   | None -> assert false
758                   | Some (_, gty') ->
759                       if gty = gty' then no_progress variant tl
760 (* 
761 (prerr_endline (string_of_int n);
762  prerr_endline (CicPp.ppterm gty);
763  prerr_endline (CicPp.ppterm gty');
764  prerr_endline "---------- subst";
765  prerr_endline (CicMetaSubst.ppsubst ~metasenv:menv subst);
766  prerr_endline "---------- variant";
767  prerr_endline (CicMetaSubst.ppsubst ~metasenv:menv variant);
768  prerr_endline "---------- menv";
769  prerr_endline (CicMetaSubst.ppmetasenv [] menv); 
770                          no_progress variant tl) *)
771                       else false))
772     | _::tl -> no_progress variant tl
773   in
774     aux todo
775
776 ;;
777 let condition_for_prune_hint prune (m, s, size, don, todo, fl) =
778   let s = 
779     HExtlib.filter_map (function S (_,_,(c,_),_) -> Some c | _ -> None) todo 
780   in
781   List.for_all (fun i -> List.for_all (fun j -> i<>j) prune) s
782 ;;
783 let filter_prune_hint c l =
784   let prune = !prune_hint in
785   prune_hint := []; (* possible race... *)
786   if prune = [] then c,l
787   else 
788     cache_reset_underinspection c,      
789     List.filter (condition_for_prune_hint prune) l
790 ;;
791
792     
793
794 let
795   auto_all_solutions dbd tables universe cache context metasenv gl flags 
796 =
797   let signature =
798     List.fold_left 
799       (fun set g ->
800          MetadataConstraints.UriManagerSet.union set 
801              (MetadataQuery.signature_of metasenv g)
802        )
803       MetadataConstraints.UriManagerSet.empty gl 
804   in
805   let goals = order_new_goals metasenv [] gl CicPp.ppterm in
806   let goals = 
807     List.map 
808       (fun (x,s) -> D (x,flags.maxdepth,s)) goals 
809   in
810   let elems = [metasenv,[],1,[],goals,[]] in
811   let rec aux tables solutions cache elems flags =
812     match auto_main dbd tables context flags signature universe cache elems with
813     | Gaveup (tables,cache) ->
814         solutions,cache, tables
815     | Proved (metasenv,subst,others,tables,cache) -> 
816         if Unix.gettimeofday () > flags.timeout then
817           ((subst,metasenv)::solutions), cache, tables
818         else
819           aux tables ((subst,metasenv)::solutions) cache others flags
820   in
821   let rc = aux tables [] cache elems flags in
822     match rc with
823     | [],cache,tables -> [],cache,tables
824     | solutions, cache,tables -> 
825         let solutions = 
826           HExtlib.filter_map
827             (fun (subst,newmetasenv) ->
828               let opened = 
829                 ProofEngineHelpers.compare_metasenvs ~oldmetasenv:metasenv ~newmetasenv
830               in
831               if opened = [] then Some subst else None)
832             solutions
833         in
834          solutions,cache,tables
835 ;;
836
837 (******************* AUTO ***************)
838
839
840 let auto dbd flags metasenv tables universe cache context metasenv gl =
841   let initial_time = Unix.gettimeofday() in  
842   let signature =
843     List.fold_left 
844       (fun set g ->
845          MetadataConstraints.UriManagerSet.union set 
846              (MetadataQuery.signature_of metasenv g)
847        )
848       MetadataConstraints.UriManagerSet.empty gl 
849   in
850   let goals = order_new_goals metasenv [] gl CicPp.ppterm in
851   let goals = List.map (fun (x,s) -> D(x,flags.maxdepth,s)) goals in
852   let elems = [metasenv,[],1,[],goals,[]] in
853   match auto_main dbd tables context flags signature universe cache elems with
854   | Proved (metasenv,subst,_, tables,cache) -> 
855       debug_print(lazy
856         ("TIME:"^string_of_float(Unix.gettimeofday()-.initial_time)));
857       Some (subst,metasenv), cache
858   | Gaveup (tables,cache) -> 
859       debug_print(lazy
860         ("TIME:"^string_of_float(Unix.gettimeofday()-.initial_time)));
861       None,cache
862 ;;
863
864 let auto_tac ~(dbd:HSql.dbd) ~params:(univ,params) ~automation_cache (proof, goal) =
865   let flags = flags_of_params params () in
866   let use_library = flags.use_library in
867   let universe, tables, cache =
868     init_cache_and_tables 
869      ~dbd ~use_library ~use_context:(not flags.skip_context)
870      automation_cache univ (proof, goal) 
871   in
872   let _,metasenv,subst,_,_, _ = proof in
873   let _,context,goalty = CicUtil.lookup_meta goal metasenv in
874   let signature = MetadataQuery.signature_of metasenv goal in
875   let signature = 
876     List.fold_left 
877       (fun set t ->
878          let ty, _ = 
879            CicTypeChecker.type_of_aux' metasenv context t 
880              CicUniv.oblivion_ugraph
881          in
882          MetadataConstraints.UriManagerSet.union set 
883            (MetadataConstraints.constants_of ty)
884        )
885       signature univ
886   in
887   let tables,cache =
888     if flags.close_more then
889       close_more 
890         tables context (proof, goal) 
891           (auto_all_solutions dbd) signature universe cache 
892     else tables,cache in
893   let initial_time = Unix.gettimeofday() in
894   let (_,oldmetasenv,_,_,_, _) = proof in
895     hint := None;
896   let elem = 
897     metasenv,subst,1,[],[D (goal,flags.maxdepth,P)],[]
898   in
899   match auto_main dbd tables context flags signature universe cache [elem] with
900     | Proved (metasenv,subst,_, tables,cache) -> 
901         debug_print (lazy 
902           ("TIME:"^string_of_float(Unix.gettimeofday()-.initial_time)));
903         let proof,metasenv =
904         ProofEngineHelpers.subst_meta_and_metasenv_in_proof
905           proof goal subst metasenv
906         in
907         let opened = 
908           ProofEngineHelpers.compare_metasenvs ~oldmetasenv
909             ~newmetasenv:metasenv
910         in
911           proof,opened
912     | Gaveup (tables,cache) -> 
913         debug_print
914           (lazy ("TIME:"^
915             string_of_float(Unix.gettimeofday()-.initial_time)));
916         raise (ProofEngineTypes.Fail (lazy "Auto gave up"))
917 ;;
918 *)
919
920
921 type th_cache = (NCic.context * InvRelDiscriminationTree.t) list
922
923 let keys_of_term status t =
924   let status, orig_ty = typeof status (ctx_of t) t in
925   let _, ty, _ = saturate ~delta:max_int status orig_ty in
926   let keys = [ty] in
927   let keys = 
928     let _, ty = term_of_cic_term status ty (ctx_of ty) in
929     match ty with
930     | NCic.Const (NReference.Ref (_,NReference.Def h)) 
931     | NCic.Appl (NCic.Const(NReference.Ref(_,NReference.Def h))::_) 
932        when h > 0 ->
933          let _,ty,_= saturate status ~delta:(h-1) orig_ty in
934          ty::keys
935     | _ -> keys
936   in
937   status, keys
938 ;;
939
940 let mk_th_cache status gl = 
941   List.fold_left 
942     (fun (status, acc) g ->
943        let gty = get_goalty status g in
944        let ctx = ctx_of gty in
945        debug_print(lazy("th cache for: "^ppterm status gty));
946        debug_print(lazy("th cache in: "^ppcontext status ctx));
947        if List.mem_assq ctx acc then status, acc else
948          let idx = InvRelDiscriminationTree.empty in
949          let status,_,idx = 
950            List.fold_left 
951              (fun (status, i, idx) _ -> 
952                 let t = mk_cic_term ctx (NCic.Rel i) in
953                 debug_print(lazy("indexing: "^ppterm status t));
954                 let status, keys = keys_of_term status t in
955                 let idx =
956                   List.fold_left (fun idx k -> 
957                     InvRelDiscriminationTree.index idx k t) idx keys
958                 in
959                 status, i+1, idx)
960              (status, 1, idx) ctx
961           in
962          status, (ctx, idx) :: acc)
963     (status,[]) gl
964 ;;
965
966 let add_to_th t c ty = 
967   let key_c = ctx_of t in
968   if not (List.mem_assq key_c c) then
969       (key_c ,InvRelDiscriminationTree.index 
970                InvRelDiscriminationTree.empty ty t ) :: c 
971   else
972     let rec replace = function
973       | [] -> []
974       | (x, idx) :: tl when x == key_c -> 
975           (x, InvRelDiscriminationTree.index idx ty t) :: tl
976       | x :: tl -> x :: replace tl
977     in 
978       replace c
979 ;;
980
981 let pp_idx status idx =
982    InvRelDiscriminationTree.iter idx
983       (fun k set ->
984          debug_print(lazy("K: " ^ NCicInverseRelIndexable.string_of_path k));
985          Ncic_termSet.iter 
986            (fun t -> debug_print(lazy("\t"^ppterm status t))) 
987            set)
988 ;;
989
990 let pp_th status = 
991   List.iter 
992     (fun ctx, idx ->
993        debug_print(lazy( "-----------------------------------------------"));
994        debug_print(lazy( (NCicPp.ppcontext ~metasenv:[] ~subst:[] ctx)));
995        debug_print(lazy( "||====>  "));
996        pp_idx status idx)
997 ;;
998
999
1000 let search_in_th gty th = 
1001   let c = ctx_of gty in
1002   let rec aux acc = function
1003    | [] -> Ncic_termSet.elements acc
1004    | (_::tl) as k ->
1005        try 
1006          let idx = List.assq k th in
1007          let acc = Ncic_termSet.union acc 
1008            (InvRelDiscriminationTree.retrieve_unifiables idx gty)
1009          in
1010          aux acc tl
1011        with Not_found -> aux acc tl
1012   in
1013     aux Ncic_termSet.empty c
1014 ;;
1015 type cache_examination_result =
1016   [ `Failed_in of int
1017   | `UnderInspection 
1018   | `Succeded of NCic.term
1019   | `NotFound
1020   ]
1021
1022 type sort = T | P
1023 type goal = int * sort (* goal, depth, sort *)
1024 type fail = goal * cic_term
1025 type candidate = int * Ast.term (* unique candidate number, candidate *)
1026
1027 type 'a op = 
1028   (* goal has to be proved *)
1029   | D of goal 
1030   (* goal has to be cached as a success obtained using candidate as the first
1031    * step *)
1032   | S of goal * (#tac_status as 'a)
1033          (* * cic_term * candidate (* int was minsize *) *)
1034   | L of goal * (#tac_status as 'a)
1035
1036 let pp_goal (g,_) = string_of_int g
1037 let pp_item = function
1038   | D g -> "D" ^ pp_goal g
1039   | S (g,_) -> "S" ^ pp_goal g 
1040   | L (g,_) -> "L" ^ pp_goal g 
1041
1042 type flags = {
1043         do_types : bool; (* solve goals in Type *)
1044         maxwidth : int;
1045         maxsize  : int;
1046         maxdepth : int;
1047         timeout  : float;
1048 }
1049
1050 type 'a tree_status = #tac_status as 'a * int * int
1051 type 'a tree_item = 'a op
1052
1053 type 'a and_pos = 
1054         (AndOrTree.andT, 'a tree_status, 'a tree_item) AndOrTree.position
1055 type 'a or_pos = 
1056         (AndOrTree.orT, 'a tree_status, 'a tree_item) AndOrTree.position
1057
1058 type 'a auto_status = 'a and_pos * th_cache
1059
1060 type 'a auto_result = 
1061   | Gaveup 
1062   | Proved of (#tac_status as 'a) * 'a auto_status option (* alt. proofs *)
1063
1064 let close_failures _ c = c;;
1065 let prunable _ _ _ = false;;
1066 let cache_examine cache gty = `Notfound;;
1067 let put_in_subst s _ _ _  = s;;
1068 let add_to_cache_and_del_from_orlist_if_green_cut _ _ c _ _ o f _ = c, o, f, false ;; 
1069 let cache_add_underinspection c _ _ = c;;
1070 let equational_case _ _ _ _ _ _ = [];;
1071 let only _ _ _ = true;;
1072
1073 let candidate_no = ref 0;;
1074
1075 let sort_new_elems l = 
1076   List.sort (fun (_,_,_,_,l1) (_,_,_,_,l2) -> List.length l1 - List.length l2) l
1077 ;;
1078
1079 let try_candidate flags depth status t g =
1080  try
1081    debug_print ~depth (lazy ("try " ^ CicNotationPp.pp_term t));
1082    let status = NTactics.focus_tac [g] status in
1083    let status = NTactics.apply_tac ("",0,t) status in
1084    let open_goals = head_goals status#stack in
1085    debug_print ~depth
1086      (lazy ("success: "^String.concat " "(List.map string_of_int open_goals)));
1087    if List.length open_goals > flags.maxwidth || 
1088       (depth = flags.maxdepth && open_goals <> []) then
1089       (debug_print ~depth (lazy "pruned immediately"); None)
1090    else
1091      (incr candidate_no;
1092       Some ((!candidate_no,t),status,open_goals))
1093  with Error (msg,exn) -> debug_print ~depth (lazy "failed"); None
1094 ;;
1095
1096 let rec mk_irl n = function
1097   | [] -> []
1098   | _ :: tl -> NCic.Rel n :: mk_irl (n+1) tl
1099 ;;
1100
1101 let get_candidates status cache_th signature gty =
1102   let universe = status#auto_cache in
1103   let context = ctx_of gty in
1104   let _, raw_gty = term_of_cic_term status gty context in
1105   let cands = 
1106     NDiscriminationTree.DiscriminationTree.retrieve_unifiables universe raw_gty
1107   in
1108   let cands = 
1109     List.filter (only signature context) 
1110       (NDiscriminationTree.TermSet.elements cands)
1111   in
1112   List.map (fun t -> 
1113      let _status, t = term_of_cic_term status t context in Ast.NCic t) 
1114      (search_in_th gty cache_th)
1115   @ 
1116   List.map (function NCic.Const r -> Ast.NRef r | _ -> assert false) cands
1117 ;;
1118
1119 let applicative_case depth signature status flags g gty cache = 
1120   let candidates = get_candidates status cache signature gty in
1121   debug_print ~depth
1122     (lazy ("candidates: " ^ string_of_int (List.length candidates)));
1123   let elems = 
1124     List.fold_left 
1125       (fun elems cand ->
1126         match try_candidate flags depth status cand g with
1127         | None -> elems
1128         | Some x -> x::elems)
1129       [] candidates
1130   in
1131   elems
1132 ;;
1133 let calculate_goal_ty (goalno,_) status = 
1134   try Some (get_goalty status goalno)
1135   with Error _ -> None
1136 ;;
1137
1138 let equational_and_applicative_case
1139   signature flags status g depth gty cache 
1140 =
1141  let elems = 
1142   if false (*is_equational_case gty flags*) then
1143     let elems =
1144       equational_case
1145         signature status flags g gty cache 
1146     in
1147     let more_elems =
1148         applicative_case depth
1149           signature status flags g gty cache 
1150     in
1151       elems@more_elems
1152   else
1153     let elems =
1154       (*match LibraryObjects.eq_URI () with
1155       | Some _ ->
1156          smart_applicative_case dbd tables depth s fake_proof goalno 
1157            gty m context signature universe cache flags
1158       | None -> *)
1159          applicative_case depth
1160           signature status flags g gty cache 
1161     in
1162       elems
1163  in
1164  let elems = 
1165    List.map (fun c,s,gl -> 
1166        c,1,1,s,List.map (fun i -> 
1167                       let sort = 
1168                        match calculate_goal_ty (i,()) s with
1169                        | None -> assert false
1170                        | Some gty ->
1171                            let _, sort = typeof s (ctx_of gty) gty in
1172                            match term_of_cic_term s sort (ctx_of sort) with
1173                            | _, NCic.Sort NCic.Prop -> P
1174                            | _ -> (*T*)P
1175                       in
1176                i,sort) gl) elems 
1177  in
1178  let elems = sort_new_elems elems in
1179  elems, cache
1180 ;;
1181
1182
1183 let d_goals l =
1184   let rec aux acc = function
1185     | (D g)::tl -> aux (acc@[g]) tl
1186     | (S _|L _)::tl -> aux acc tl
1187     | [] -> acc
1188   in
1189     aux [] l
1190 ;;
1191 let prop_only l =
1192   List.filter (function (_,P) -> true | _ -> false) l
1193 ;;
1194
1195 let rec guess_name name ctx = 
1196   if name = "_" then guess_name "auto" ctx else
1197   if not (List.mem_assoc name ctx) then name else
1198   guess_name (name^"'") ctx
1199 ;;
1200
1201 let intro_case status gno gty depth cache name =
1202    let status = NTactics.focus_tac [gno] status in
1203    let status = NTactics.intro_tac (guess_name name (ctx_of gty)) status in
1204    let open_goals = head_goals status#stack in
1205    assert (List.length open_goals  = 1);
1206    let open_goal = List.hd open_goals in
1207    let ngty = get_goalty status open_goal in
1208    let ctx = ctx_of ngty in
1209    let t = mk_cic_term ctx (NCic.Rel 1) in
1210    let status, keys = keys_of_term status t in
1211    let cache = List.fold_left (add_to_th t) cache keys in
1212    debug_print ~depth (lazy ("intro: "^ string_of_int open_goal));
1213    incr candidate_no;
1214     (* XXX calculate the sort *)
1215    [(!candidate_no,Ast.Implicit `JustOne),0,0,status,[open_goal,P]],
1216    cache
1217 ;;
1218                       
1219 let do_something signature flags s gno depth gty cache =
1220   let _s, raw_gty = term_of_cic_term s gty (ctx_of gty) in
1221   match raw_gty with
1222   | NCic.Prod (name,_,_) -> intro_case s gno gty depth cache name
1223   | _ -> 
1224       equational_and_applicative_case signature flags s gno depth gty cache
1225 ;;
1226
1227 module T = ZipTree
1228 module Z = AndOrTree
1229
1230 let img_counter = ref 0 ;;
1231 let show pos =
1232     incr img_counter;
1233     let file = ("/tmp/a"^string_of_int !img_counter^".dot") in
1234     debug_print (lazy("generating " ^ file));
1235     debug_do (fun () ->
1236     let oc = open_out file in
1237     let fmt = Format.formatter_of_out_channel oc in
1238     GraphvizPp.Dot.header fmt;
1239     Z.dump pp_item pos fmt;
1240     GraphvizPp.Dot.trailer fmt;
1241     Format.fprintf fmt "@?";
1242     close_out oc;
1243     ignore(Sys.command ("dot -Tpng "^file^" > "^file^".png")); 
1244     (*ignore(Sys.command ("eog "^file^".png"))*))
1245 ;;
1246
1247 let rightmost_bro pred pos =
1248  let rec last acc pos = 
1249    let acc = if pred pos then Some pos else acc in
1250    match Z.right pos with
1251    | None -> acc
1252    | Some pos -> last acc pos
1253  in
1254    last None pos
1255 ;;
1256
1257 let leftmost_bro pred pos =
1258  let rec fst pos = 
1259    if pred pos then Some pos else 
1260      match Z.right pos with
1261      | None ->  None
1262      | Some pos -> fst pos
1263  in
1264    fst pos
1265 ;;
1266
1267 let rec first_left_mark_L_as_D pred pos =
1268   if pred pos then 
1269       Some pos
1270   else 
1271     let pos = 
1272       match Z.getA pos with
1273       | s,L (g,_) -> 
1274            Z.inject T.Nil (Z.setA s (D g) pos)
1275       | _ -> pos
1276     in
1277     match Z.left pos with 
1278     | None -> None 
1279     | Some pos -> 
1280         first_left_mark_L_as_D pred pos
1281 ;;
1282
1283 let is_oS pos = 
1284  match Z.getO pos with
1285  | S _ -> true
1286  | D _ | L _ -> false
1287 ;;
1288
1289
1290 let is_aS pos = 
1291  match Z.getA pos with
1292  | _,S _ -> true
1293  | _,D _ | _,L _ -> false
1294 ;;
1295
1296 let is_not_oS x = not (is_oS x);;
1297 let is_not_aS x = not (is_aS x);;
1298
1299 let is_oL pos = match Z.getO pos with L _ -> true | _ -> false ;;
1300 let is_aL pos = match Z.getA pos with _,L _ -> true | _ -> false ;;
1301
1302 let is_not_oL x = not (is_oL x) ;;
1303 let is_not_aL x = not (is_aL x) ;;
1304
1305 let rec forall_left pred pos = 
1306   match Z.left pos with
1307   | None -> true
1308   | Some pos -> if pred pos then forall_left pred pos else false
1309 ;;
1310
1311   
1312 let rec product = function
1313   | [] -> []
1314   | ((g,s) :: tl) as l -> (s,List.map fst l) :: product tl
1315 ;;
1316
1317 let has_no_alternatives (pos : 'a and_pos) = 
1318   match Z.getA pos with
1319   | _, L _ -> true
1320   | _ -> false
1321 ;;
1322
1323 let rec collect_left_up (pos : 'a and_pos) =
1324   match Z.left pos with
1325   | Some pos -> 
1326      (match Z.getA pos with
1327      | _, L (g,s) -> (g,s) :: collect_left_up pos
1328      | _ -> [])
1329   | None -> 
1330       match Z.upA pos with
1331       | None -> [] (* root *)
1332       | Some pos -> collect_left_up (Z.upO pos)
1333 ;;
1334
1335 let compute_failed_goals (pos : 'a and_pos) =
1336   let curr = match Z.getA pos with (s,_,_),D g -> (g,s) | _ -> assert false in
1337   product (List.rev (curr :: collect_left_up pos) )
1338 ;;
1339
1340 let pp_failures l =
1341   debug_print (lazy("CACHE FAILURES/UNDERINSPECTION"));
1342   List.iter (fun (s,gl) -> 
1343     debug_print (lazy("FAIL: " ^
1344     String.concat " , " (List.map (fun g ->
1345     match calculate_goal_ty g s with
1346     | None -> 
1347         (try 
1348           let (i,_) = g in 
1349           let _,_,_,subst,_ = s#obj in
1350           let _,cc,_,ty = NCicUtils.lookup_subst i subst in
1351           let ty = mk_cic_term cc ty in
1352           string_of_int i^":"^ppterm s ty
1353         with NCicUtils.Subst_not_found _ -> "XXXX")
1354     | Some gty ->
1355        let s, gty = apply_subst s (ctx_of gty) gty in
1356        string_of_int (fst g)^":"^ppterm s gty) gl)))) 
1357     l
1358 ;;
1359
1360 let is_closed pos = 
1361   match Z.getA pos with
1362   | (s,_,_),S (g,_) 
1363   | (s,_,_),D g ->
1364       (match calculate_goal_ty g s with
1365       | None -> true
1366       | Some gty -> metas_of_term s gty = [])
1367   | _, L _ -> assert false
1368 ;;
1369
1370 let auto_main flags signature (pos : 'a and_pos) cache =
1371   let solved g depth size s (pos : 'a and_pos) =
1372     Z.inject (T.Node(`Or,[D g,T.Node(`And(s,depth,size),[])])) pos
1373   in
1374   let failed (pos : 'a and_pos) =
1375     pp_failures (compute_failed_goals pos);
1376     Z.inject (T.Node(`Or,[])) pos
1377   in
1378
1379   let rec next ~unfocus (pos : 'a and_pos) cache = 
1380     (* TODO: process USER HINT is any *)
1381     match Z.downA pos with
1382     | Z.Unexplored -> attack pos cache (Z.getA pos)
1383     | Z.Alternatives pos -> nextO ~unfocus pos cache
1384
1385   and nextO ~unfocus (pos : 'a or_pos) cache =
1386     match Z.getO pos with
1387     | S _ | L _ -> assert false (* XXX set to Nil when backtrack *)
1388     | D g -> 
1389         match Z.downO pos with
1390         | Z.Solution (s,_,_) -> move_solution_up ~unfocus true s pos cache 
1391         | Z.Todo pos -> next ~unfocus:true pos cache 
1392
1393   and next_choice_point (pos : 'a and_pos) cache  =
1394
1395     let rec global_choice_point (pos : 'a and_pos) : 'a auto_result =
1396 (*             prerr_endline "global"; show pos; *)
1397       match Z.upA pos with
1398       | None -> Gaveup
1399       | Some alts -> 
1400           let alts = Z.inject T.Nil alts in
1401           let alts = 
1402             match Z.getO alts with
1403             | S (s,g) -> Z.setO (L (s,g)) alts 
1404             | D (g) -> Z.setO (L (g,Obj.magic g)) alts 
1405                        (* L (and other marks) for OR should have no arguments *)
1406             | L _ -> assert false
1407           in
1408           match Z.right alts with
1409           | None -> 
1410              let upalts = Z.upO alts in
1411              let upalts = Z.inject T.Nil upalts in
1412              let upalts = 
1413                match Z.getA upalts with
1414                | s,S (a,b) -> Z.setA s (L (a,b)) upalts 
1415                | _,L _ -> assert false
1416                | s,D (a) -> Z.setA s (L (a,Obj.magic a)) upalts 
1417              in
1418              backtrack upalts
1419           | Some pos -> 
1420               match Z.downO pos with
1421               | Z.Solution (s,_,_) -> 
1422                     move_solution_up ~unfocus:false true s pos cache
1423               | Z.Todo pos -> next ~unfocus:true pos cache 
1424
1425     and backtrack (pos : 'a and_pos) : 'a auto_result =
1426 (*             prerr_endline "backtrack"; show pos; *)
1427       let pos = Z.inject T.Nil pos in
1428       let pos = 
1429         match Z.getA pos with 
1430         | s,D g | s, S (g,_) | s,L(g,_) -> Z.setA s (D g) pos 
1431       in
1432       match first_left_mark_L_as_D is_aS pos with 
1433       | None -> global_choice_point pos
1434       | Some pos ->
1435          let rec local_choice_point pos =
1436 (*             prerr_endline "local"; show pos; *)
1437            match Z.downA pos with
1438            | Z.Unexplored -> attack pos cache (Z.getA pos)
1439            | Z.Alternatives alts ->  
1440                match leftmost_bro is_not_oL alts with
1441                | None -> assert false (* is not L, thus has alternatives *)
1442                | Some pos ->
1443                    let is_D = is_not_oS pos in
1444                    match if is_D then Z.downO pos else Z.downOr pos with
1445                    | Z.Solution (s,_,_) -> assert(is_D);
1446                         move_solution_up ~unfocus:false true s pos cache
1447                    | Z.Todo pos when is_D -> attack pos cache (Z.getA pos)
1448                    | Z.Todo pos ->
1449                         match first_left_mark_L_as_D is_aS pos with
1450                         | Some pos -> local_choice_point pos
1451                         | None -> assert false
1452          in
1453            local_choice_point pos
1454     in
1455       backtrack pos
1456
1457   and next_choice (pos : 'a and_pos) cache = 
1458     next_choice_point pos cache
1459
1460   and move_solution_up 
1461       ~unfocus are_sons_L
1462       (status : #tac_status as 'a) (pos : 'a or_pos) cache 
1463   =
1464     let pos = (* mark as solved *)
1465       match Z.getO pos with
1466       | L _ -> assert false (* XXX  *) 
1467       | S (g,_) 
1468       | D g -> 
1469           if are_sons_L then 
1470              Z.inject T.Nil (Z.setO (L (g,status)) pos)
1471           else 
1472              Z.setO (S (g,status)) pos 
1473     in
1474     let has_alternative_or = match Z.right pos with None -> false | _ -> true in
1475     let pos = Z.upO pos in
1476     let are_all_lbro_L = forall_left is_aL pos in
1477     let has_no_alternative = 
1478       ((not has_alternative_or) && are_sons_L) ||
1479       is_closed pos
1480     in
1481     match Z.getA pos with
1482     | _, L _ -> assert false
1483     | (_, size, depth), S (g,_) 
1484        (* S if already solved and then solved again because of a backtrack *)
1485     | (_, size, depth), D g -> 
1486         let newg = 
1487           if has_no_alternative then (L (g,status)) else (S (g,status))in
1488         (* TODO: cache success g *)
1489         let pos = if has_no_alternative then Z.inject T.Nil pos else pos in
1490          let status = if unfocus then NTactics.unfocus_tac status else status
1491          in
1492         let news = status,size,depth in
1493         let pos = Z.setA news newg pos in
1494         match Z.right pos with
1495         | Some pos -> next ~unfocus:true pos cache
1496         | None -> 
1497             match Z.upA pos with
1498             | None -> Proved (status, Some (pos,cache))
1499             | Some pos -> 
1500                move_solution_up 
1501                  ~unfocus:true (has_no_alternative && are_all_lbro_L)
1502                  status pos cache 
1503
1504   and attack pos cache and_item =
1505     (* show pos; uncomment to show the tree *)
1506     match and_item with
1507     | _, S _ -> assert false (* next would close the proof or give a D *) 
1508     | _, L _ -> assert false (* L is a final solution *)
1509     | (_, depth, _),_ when Unix.gettimeofday () > flags.timeout ->
1510         debug_print ~depth (lazy ("fail timeout"));
1511         Gaveup 
1512     | (s, depth, width), D (_, T as g) when not flags.do_types -> 
1513         debug_print ~depth (lazy "skip goal in Type");
1514         next ~unfocus:false (solved g depth width s pos) cache
1515     | (_,depth,_), D _ when depth > flags.maxdepth -> 
1516         debug_print ~depth (lazy "fail depth");
1517         next_choice (failed pos) cache
1518     | (_,depth,size), D _ when size > flags.maxsize -> 
1519         debug_print ~depth (lazy "fail size");
1520         next_choice (failed pos) cache
1521     | (s,depth,size), D (gno,_ as g) -> 
1522         assert (Z.eject pos = T.Nil); (*subtree must be unexplored *)
1523         match calculate_goal_ty g s with
1524         | None -> 
1525            debug_print ~depth (lazy("success side effect: "^string_of_int gno));
1526            next ~unfocus:false (solved g depth size s pos) cache
1527         | Some gty ->
1528            let s, gty = apply_subst s (ctx_of gty) gty in
1529            debug_print ~depth (lazy ("EXAMINE: "^ ppterm s gty));
1530            match cache_examine cache gty with
1531            | `Failed_in d when d <= depth -> 
1532                debug_print ~depth(lazy("fail depth (c): "^string_of_int gno));
1533                next_choice (failed pos) cache
1534            | `UnderInspection -> 
1535                debug_print ~depth (lazy("fail loop: "^string_of_int gno));
1536                next_choice (failed pos) cache
1537            | `Succeded t -> 
1538                debug_print ~depth (lazy("success (c): "^string_of_int gno));
1539                let s = put_in_subst s g t gty in
1540                next ~unfocus:true (solved g depth size s pos) cache
1541            | `Notfound 
1542            | `Failed_in _ -> 
1543                (* more depth than before or first time we see the goal *)
1544                if prunable s gty () then
1545                  (debug_print ~depth (lazy( "fail one father is equal"));
1546                   next_choice (failed pos) cache)
1547                else
1548                let cache = cache_add_underinspection cache gty depth in
1549                debug_print ~depth (lazy ("INSPECTING: " ^ 
1550                  string_of_int gno ^ "("^ string_of_int size ^ ") "));
1551                let subgoals, cache = 
1552                  do_something signature flags s gno depth gty cache
1553                in
1554                if subgoals = [] then (* this goal has failed *)
1555                  next_choice (failed pos) cache
1556                else
1557                  let size_gl l = List.length (prop_only l) in
1558                  let subtrees = 
1559                    List.map
1560                      (fun (_cand,depth_incr,size_mult,s,gl) ->
1561                        D(gno,P), 
1562                        T.Node (`And 
1563                           (s,depth+depth_incr,size+size_mult*(size_gl gl)), 
1564                                List.map (fun g -> D g,T.Nil) gl))
1565                      subgoals
1566                  in
1567                   next ~unfocus:true 
1568                     (Z.inject (T.Node (`Or,subtrees)) pos) cache
1569   in
1570     (next ~unfocus:true pos cache : 'a auto_result)
1571 ;;
1572
1573 let int name l def = 
1574   try int_of_string (List.assoc name l)
1575   with Failure _ | Not_found -> def
1576 ;;
1577
1578 let auto_tac ~params:(_univ,flags) status =
1579   let goals = head_goals status#stack in
1580   let status, cache = mk_th_cache status goals in
1581 (*   pp_th status cache; *)
1582 (*
1583   NDiscriminationTree.DiscriminationTree.iter status#auto_cache (fun p t -> 
1584     debug_print (lazy(
1585       NDiscriminationTree.NCicIndexable.string_of_path p ^ " |--> " ^
1586       String.concat "\n    " (List.map (
1587       NCicPp.ppterm ~metasenv:[] ~context:[] ~subst:[])
1588         (NDiscriminationTree.TermSet.elements t))
1589       )));
1590 *)
1591   let depth = int "depth" flags 3 in 
1592   let size  = int "size" flags 10 in 
1593   let width = int "width" flags (3+List.length goals) in 
1594   (* XXX fix sort *)
1595   let goals = List.map (fun i -> D(i,P), T.Nil) goals in
1596   let elems = Z.start (T.Node (`And(status,0,0),goals)) in
1597   let signature = () in
1598   let flags = { 
1599           maxwidth = width;
1600           maxsize = size;
1601           maxdepth = depth;
1602           timeout = Unix.gettimeofday() +. 3000.;
1603           do_types = false; 
1604   } in
1605   let rec up_to x y =
1606     if x > y then raise (Error (lazy "auto gave up", None))
1607     else
1608       let _ = debug_print (lazy("\n\nRound "^string_of_int x^"\n")) in
1609       let flags = { flags with maxdepth = x } in
1610       match auto_main flags signature elems cache with
1611       | Gaveup -> up_to (x+1) y
1612       | Proved (s, _) -> 
1613           HLog.debug ("proved at depth " ^ string_of_int x);
1614           let stack = 
1615             match s#stack with
1616             | (g,t,k,f) :: rest -> (filter_open g,t,k,f):: rest
1617             | _ -> assert false
1618           in
1619           s#set_stack stack
1620   in
1621     up_to depth depth
1622 ;;
1623
1624 let rec rm_assoc n = function
1625   | [] -> assert false
1626   | (x,i)::tl when n=x -> i,tl
1627   | p::tl -> let i,tl = rm_assoc n tl in i,p::tl
1628 ;;
1629
1630 let merge canonicals elements n m =
1631   let cn,canonicals = rm_assoc n canonicals in
1632   let cm,canonicals = rm_assoc m canonicals in
1633   let ln,elements = rm_assoc cn elements in
1634   let lm,elements = rm_assoc cm elements in
1635   let canonicals = 
1636     (n,cm)::(m,cm)::List.map 
1637       (fun (x,xc) as p  -> 
1638          if xc = cn then (x,cm) else p) canonicals
1639   in 
1640   let elements = (cn,ln@lm)::elements 
1641   in
1642     canonicals,elements
1643 ;;
1644
1645 let clusters f l =
1646   let canonicals = List.map (fun x -> (x,x)) l in
1647   let elements = List.map (fun x -> (x,[x])) l in
1648    List.fold_left 
1649      (fun (canonicals,elements) x ->
1650        let dep = f x in
1651          List.fold_left 
1652            (fun (canonicals,elements) d ->
1653               merge canonicals elements d x) 
1654            (canonicals,elements) dep)
1655      (canonicals,elements) l
1656 ;;
1657
1658 let group_by_tac ~eq_predicate ~action:tactic status = 
1659  let goals = head_goals status#stack in
1660  if List.length goals < 2 then tactic status 
1661  else
1662   let eq_predicate = eq_predicate status in
1663   let rec aux classes = function
1664     | [] -> classes
1665     | g :: tl ->
1666        try
1667          let c = List.find (fun c -> eq_predicate c g) classes in
1668          let classes = List.filter ((<>) c) classes in
1669          aux ((g::c) :: classes) tl
1670        with Not_found -> aux ([g] :: classes) tl
1671   in
1672   let classes = aux [] goals in
1673   List.iter 
1674    (fun l -> 
1675       HLog.debug ("cluster:" ^ String.concat "," (List.map string_of_int l)))
1676    classes;
1677   let pos_of l1 l2 = 
1678     let l2 = HExtlib.list_mapi (fun x i -> x,i+1) l2 in
1679     List.map (fun x -> List.assoc x l2) l1
1680   in
1681   NTactics.block_tac ([ NTactics.branch_tac ~force:false]
1682     @
1683     HExtlib.list_concat ~sep:[NTactics.shift_tac]
1684       (List.map (fun gl-> [NTactics.pos_tac (pos_of gl goals); tactic]) classes)
1685     @
1686     [ NTactics.merge_tac ]) status
1687 ;;
1688
1689 module IntSet = Set.Make(struct type t = int let compare = compare end)
1690
1691 let type_dependency status gl g =
1692   let rec closure acc = function
1693     | [] -> acc
1694     | x::l when IntSet.mem x acc -> closure acc l
1695     | x::l ->
1696         let acc = IntSet.add x acc in
1697         let gty = get_goalty status x in
1698         let deps = metas_of_term status gty in
1699         closure acc (deps @ l)
1700   in
1701   not (IntSet.is_empty 
1702         (IntSet.inter
1703           (closure IntSet.empty gl) 
1704           (closure IntSet.empty [g])))
1705 ;;
1706
1707 let auto_tac ~params = 
1708   group_by_tac ~eq_predicate:type_dependency ~action:(auto_tac ~params)
1709 ;;
1710
1711 (* ========================= dispatching of auto/auto_paramod ============ *)
1712 let auto_tac ~params:(_,flags as params) ?trace_ref =
1713   if List.mem_assoc "paramodulation" flags then 
1714     auto_paramod_tac ~params 
1715   else if List.mem_assoc "demod" flags then 
1716     NnAuto.demod_tac ~params 
1717   else if List.mem_assoc "paramod" flags then 
1718     NnAuto.paramod_tac ~params 
1719   else if List.mem_assoc "fast_paramod" flags then 
1720     NnAuto.fast_eq_check_tac ~params  
1721   else if List.mem_assoc "slir" flags then 
1722     NnAuto.auto_tac ~params ?trace_ref
1723   else 
1724     auto_tac ~params
1725 ;;
1726
1727 (* EOF *)