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