]> matita.cs.unibo.it Git - helm.git/blob - helm/ocaml/cic_proof_checking/cicReduction.ml
New reduction strategy (that behaves much better during simplification).
[helm.git] / helm / ocaml / cic_proof_checking / cicReduction.ml
1 (* Copyright (C) 2000, HELM Team.
2  * 
3  * This file is part of HELM, an Hypertextual, Electronic
4  * Library of Mathematics, developed at the Computer Science
5  * Department, University of Bologna, Italy.
6  * 
7  * HELM is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * 
12  * HELM is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with HELM; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20  * MA  02111-1307, USA.
21  * 
22  * For details, see the HELM World-Wide-Web page,
23  * http://cs.unibo.it/helm/.
24  *)
25
26 (* $Id$ *)
27
28 (* TODO unify exceptions *)
29
30 exception WrongUriToInductiveDefinition;;
31 exception Impossible of int;;
32 exception ReferenceToConstant;;
33 exception ReferenceToVariable;;
34 exception ReferenceToCurrentProof;;
35 exception ReferenceToInductiveDefinition;;
36
37 let debug = false
38 let profile = false
39 let debug_print s = if debug then prerr_endline (Lazy.force s)
40
41 let fdebug = ref 1;;
42 let debug t env s =
43  let rec debug_aux t i =
44   let module C = Cic in
45   let module U = UriManager in
46    CicPp.ppobj (C.Variable ("DEBUG", None, t, [], [])) ^ "\n" ^ i
47  in
48   if !fdebug = 0 then
49    debug_print (lazy (s ^ "\n" ^ List.fold_right debug_aux (t::env) ""))
50 ;;
51
52 module type Strategy =
53  sig
54   type stack_term
55   type env_term
56   type ens_term
57   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
58   val to_env : config -> env_term
59   val to_ens : config -> ens_term
60   val from_stack : stack_term -> config
61   val from_stack_list_for_unwind :
62    unwind: (config -> Cic.term) ->
63    stack_term list -> Cic.term list
64   val from_env : env_term -> config
65   val from_env_for_unwind :
66    unwind: (config -> Cic.term) ->
67    env_term -> Cic.term
68   val from_ens : ens_term -> config
69   val from_ens_for_unwind :
70    unwind: (config -> Cic.term) ->
71    ens_term -> Cic.term
72   val stack_to_env :
73    reduce: (config -> config) ->
74    unwind: (config -> Cic.term) ->
75    stack_term -> env_term
76   val compute_to_env :
77    reduce: (config -> config) ->
78    unwind: (config -> Cic.term) ->
79    int -> env_term list -> ens_term Cic.explicit_named_substitution ->
80     Cic.term -> env_term
81   val compute_to_stack :
82    reduce: (config -> config) ->
83    unwind: (config -> Cic.term) ->
84    config -> stack_term
85  end
86 ;;
87
88 module CallByValueByNameForUnwind =
89  struct
90   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
91   and stack_term = config
92   and env_term = config * config (* cbv, cbn *)
93   and ens_term = config * config (* cbv, cbn *)
94
95   let to_env c = c,c
96   let to_ens c = c,c
97   let from_stack config = config
98   let from_stack_list_for_unwind ~unwind l = List.map unwind l
99   let from_env (c,_) = c
100   let from_ens (c,_) = c
101   let from_env_for_unwind ~unwind (_,c) = unwind c
102   let from_ens_for_unwind ~unwind (_,c) = unwind c
103   let stack_to_env ~reduce ~unwind config = reduce config, (0,[],[],unwind config,[])
104   let compute_to_env ~reduce ~unwind k e ens t = (k,e,ens,t,[]), (k,e,ens,t,[])
105   let compute_to_stack ~reduce ~unwind config = config
106  end
107 ;;
108
109
110 module CallByNameStrategy =
111  struct
112   type stack_term = Cic.term
113   type env_term = Cic.term
114   type ens_term = Cic.term
115   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
116   let to_env v = v
117   let to_ens v = v
118   let from_stack ~unwind v = v
119   let from_stack_list ~unwind l = l
120   let from_env v = v
121   let from_ens v = v
122   let from_env_for_unwind ~unwind v = v
123   let from_ens_for_unwind ~unwind v = v
124   let stack_to_env ~reduce ~unwind v = v
125   let compute_to_stack ~reduce ~unwind k e ens t = unwind k e ens t
126   let compute_to_env ~reduce ~unwind k e ens t = unwind k e ens t
127  end
128 ;;
129
130 module CallByValueStrategy =
131  struct
132   type stack_term = Cic.term
133   type env_term = Cic.term
134   type ens_term = Cic.term
135   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
136   let to_env v = v
137   let to_ens v = v
138   let from_stack ~unwind v = v
139   let from_stack_list ~unwind l = l
140   let from_env v = v
141   let from_ens v = v
142   let from_env_for_unwind ~unwind v = v
143   let from_ens_for_unwind ~unwind v = v
144   let stack_to_env ~reduce ~unwind v = v
145   let compute_to_stack ~reduce ~unwind k e ens t = reduce (k,e,ens,t,[])
146   let compute_to_env ~reduce ~unwind k e ens t = reduce (k,e,ens,t,[])
147  end
148 ;;
149
150 module CallByValueStrategyByNameOnConstants =
151  struct
152   type stack_term = Cic.term
153   type env_term = Cic.term
154   type ens_term = Cic.term
155   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
156   let to_env v = v
157   let to_ens v = v
158   let from_stack ~unwind v = v
159   let from_stack_list ~unwind l = l
160   let from_env v = v
161   let from_ens v = v
162   let from_env_for_unwind ~unwind v = v
163   let from_ens_for_unwind ~unwind v = v
164   let stack_to_env ~reduce ~unwind v = v
165   let compute_to_stack ~reduce ~unwind k e ens =
166    function
167       Cic.Const _ as t -> unwind k e ens t    
168     | t -> reduce (k,e,ens,t,[])
169   let compute_to_env ~reduce ~unwind k e ens =
170    function
171       Cic.Const _ as t -> unwind k e ens t    
172     | t -> reduce (k,e,ens,t,[])
173  end
174 ;;
175
176 module LazyCallByValueStrategy =
177  struct
178   type stack_term = Cic.term lazy_t
179   type env_term = Cic.term lazy_t
180   type ens_term = Cic.term lazy_t
181   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
182   let to_env v = lazy v
183   let to_ens v = lazy v
184   let from_stack ~unwind v = Lazy.force v
185   let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
186   let from_env v = Lazy.force v
187   let from_ens v = Lazy.force v
188   let from_env_for_unwind ~unwind v = Lazy.force v
189   let from_ens_for_unwind ~unwind v = Lazy.force v
190   let stack_to_env ~reduce ~unwind v = v
191   let compute_to_stack ~reduce ~unwind k e ens t = lazy (reduce (k,e,ens,t,[]))
192   let compute_to_env ~reduce ~unwind k e ens t = lazy (reduce (k,e,ens,t,[]))
193  end
194 ;;
195
196 module LazyCallByValueStrategyByNameOnConstants =
197  struct
198   type stack_term = Cic.term lazy_t
199   type env_term = Cic.term lazy_t
200   type ens_term = Cic.term lazy_t
201   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
202   let to_env v = lazy v
203   let to_ens v = lazy v
204   let from_stack ~unwind v = Lazy.force v
205   let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
206   let from_env v = Lazy.force v
207   let from_ens v = Lazy.force v
208   let from_env_for_unwind ~unwind v = Lazy.force v
209   let from_ens_for_unwind ~unwind v = Lazy.force v
210   let stack_to_env ~reduce ~unwind v = v
211   let compute_to_stack ~reduce ~unwind k e ens t =
212    lazy (
213     match t with
214        Cic.Const _ as t -> unwind k e ens t    
215      | t -> reduce (k,e,ens,t,[]))
216   let compute_to_env ~reduce ~unwind k e ens t =
217    lazy (
218     match t with
219        Cic.Const _ as t -> unwind k e ens t    
220      | t -> reduce (k,e,ens,t,[]))
221  end
222 ;;
223
224 module LazyCallByNameStrategy =
225  struct
226   type stack_term = Cic.term lazy_t
227   type env_term = Cic.term lazy_t
228   type ens_term = Cic.term lazy_t
229   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
230   let to_env v = lazy v
231   let to_ens v = lazy v
232   let from_stack ~unwind v = Lazy.force v
233   let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
234   let from_env v = Lazy.force v
235   let from_ens v = Lazy.force v
236   let from_env_for_unwind ~unwind v = Lazy.force v
237   let from_ens_for_unwind ~unwind v = Lazy.force v
238   let stack_to_env ~reduce ~unwind v = v
239   let compute_to_stack ~reduce ~unwind k e ens t = lazy (unwind k e ens t)
240   let compute_to_env ~reduce ~unwind k e ens t = lazy (unwind k e ens t)
241  end
242 ;;
243
244 module
245  LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns
246 =
247  struct
248   type stack_term = reduce:bool -> Cic.term
249   type env_term = reduce:bool -> Cic.term
250   type ens_term = reduce:bool -> Cic.term
251   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
252   let to_env v =
253    let value = lazy v in
254     fun ~reduce -> Lazy.force value
255   let to_ens v =
256    let value = lazy v in
257     fun ~reduce -> Lazy.force value
258   let from_stack ~unwind v = (v ~reduce:false)
259   let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
260   let from_env v = (v ~reduce:true)
261   let from_ens v = (v ~reduce:true)
262   let from_env_for_unwind ~unwind v = (v ~reduce:true)
263   let from_ens_for_unwind ~unwind v = (v ~reduce:true)
264   let stack_to_env ~reduce ~unwind v = v
265   let compute_to_stack ~reduce ~unwind k e ens t =
266    let svalue =
267      lazy (
268       match t with
269          Cic.Const _ as t -> unwind k e ens t    
270        | t -> reduce (k,e,ens,t,[])
271      ) in
272    let lvalue =
273     lazy (unwind k e ens t)
274    in
275     fun ~reduce ->
276      if reduce then Lazy.force svalue else Lazy.force lvalue
277   let compute_to_env ~reduce ~unwind k e ens t =
278    let svalue =
279      lazy (
280       match t with
281          Cic.Const _ as t -> unwind k e ens t    
282        | t -> reduce (k,e,ens,t,[])
283      ) in
284    let lvalue =
285     lazy (unwind k e ens t)
286    in
287     fun ~reduce ->
288      if reduce then Lazy.force svalue else Lazy.force lvalue
289  end
290 ;;
291
292 module ClosuresOnStackByValueFromEnvOrEnsStrategy =
293  struct
294   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
295   and stack_term = config
296   and env_term = config
297   and ens_term = config
298
299   let to_env config = config
300   let to_ens config = config
301   let from_stack config = config
302   let from_stack_list_for_unwind ~unwind l = List.map unwind l
303   let from_env v = v
304   let from_ens v = v
305   let from_env_for_unwind ~unwind config = unwind config
306   let from_ens_for_unwind ~unwind config = unwind config
307   let stack_to_env ~reduce ~unwind config = reduce config
308   let compute_to_env ~reduce ~unwind k e ens t = (k,e,ens,t,[])
309   let compute_to_stack ~reduce ~unwind config = config
310  end
311 ;;
312
313 module ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy =
314  struct
315   type stack_term =
316    int * Cic.term list * Cic.term Cic.explicit_named_substitution * Cic.term
317   type env_term = Cic.term
318   type ens_term = Cic.term
319   type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list
320   let to_env v = v
321   let to_ens v = v
322   let from_stack ~unwind (k,e,ens,t) = unwind k e ens t
323   let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
324   let from_env v = v
325   let from_ens v = v
326   let from_env_for_unwind ~unwind v = v
327   let from_ens_for_unwind ~unwind v = v
328   let stack_to_env ~reduce ~unwind (k,e,ens,t) =
329    match t with
330       Cic.Const _ as t -> unwind k e ens t    
331     | t -> reduce (k,e,ens,t,[])
332   let compute_to_env ~reduce ~unwind k e ens t =
333    unwind k e ens t
334   let compute_to_stack ~reduce ~unwind k e ens t = (k,e,ens,t)
335  end
336 ;;
337
338 module Reduction(RS : Strategy) =
339  struct
340   type env = RS.env_term list
341   type ens = RS.ens_term Cic.explicit_named_substitution
342   type stack = RS.stack_term list
343   type config = int * env * ens * Cic.term * stack
344
345   (* k is the length of the environment e *)
346   (* m is the current depth inside the term *)
347   let rec unwind' m k e ens t = 
348    let module C = Cic in
349    let module S = CicSubstitution in
350     if k = 0 && ens = [] then
351      t
352     else 
353      let rec unwind_aux m =
354       function
355          C.Rel n as t ->
356           if n <= m then t else
357            let d =
358             try
359              Some (RS.from_env_for_unwind ~unwind (List.nth e (n-m-1)))
360             with _ -> None
361            in
362             (match d with 
363                 Some t' ->
364                  if m = 0 then t' else S.lift m t'
365               | None -> C.Rel (n-k)
366             )
367        | C.Var (uri,exp_named_subst) ->
368 (*
369 debug_print (lazy ("%%%%%UWVAR " ^ String.concat " ; " (List.map (function (uri,t) -> UriManager.string_of_uri uri ^ " := " ^ CicPp.ppterm t) ens))) ;
370 *)
371          if List.exists (function (uri',_) -> UriManager.eq uri' uri) ens then
372           CicSubstitution.lift m (RS.from_ens_for_unwind ~unwind (List.assq uri ens))
373          else
374           let params =
375             let o,_ = 
376               CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
377             in
378            (match o with
379                C.Constant _ -> raise ReferenceToConstant
380              | C.Variable (_,_,_,params,_) -> params
381              | C.CurrentProof _ -> raise ReferenceToCurrentProof
382              | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
383            )
384           in
385            let exp_named_subst' =
386             substaux_in_exp_named_subst params exp_named_subst m 
387            in
388             C.Var (uri,exp_named_subst')
389        | C.Meta (i,l) ->
390           let l' =
391            List.map
392             (function
393                 None -> None
394               | Some t -> Some (unwind_aux m t)
395             ) l
396           in
397            C.Meta (i, l')
398        | C.Sort _ as t -> t
399        | C.Implicit _ as t -> t
400        | C.Cast (te,ty) -> C.Cast (unwind_aux m te, unwind_aux m ty) (*CSC ???*)
401        | C.Prod (n,s,t) -> C.Prod (n, unwind_aux m s, unwind_aux (m + 1) t)
402        | C.Lambda (n,s,t) -> C.Lambda (n, unwind_aux m s, unwind_aux (m + 1) t)
403        | C.LetIn (n,s,t) -> C.LetIn (n, unwind_aux m s, unwind_aux (m + 1) t)
404        | C.Appl l -> C.Appl (List.map (unwind_aux m) l)
405        | C.Const (uri,exp_named_subst) ->
406           let params =
407             let o,_ = 
408               CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
409             in
410            (match o with
411                C.Constant (_,_,_,params,_) -> params
412              | C.Variable _ -> raise ReferenceToVariable
413              | C.CurrentProof (_,_,_,_,params,_) -> params
414              | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
415            )
416           in
417            let exp_named_subst' =
418             substaux_in_exp_named_subst params exp_named_subst m 
419            in
420             C.Const (uri,exp_named_subst')
421        | C.MutInd (uri,i,exp_named_subst) ->
422           let params =
423             let o,_ = 
424               CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
425             in
426            (match o with
427                C.Constant _ -> raise ReferenceToConstant
428              | C.Variable _ -> raise ReferenceToVariable
429              | C.CurrentProof _ -> raise ReferenceToCurrentProof
430              | C.InductiveDefinition (_,params,_,_) -> params
431            )
432           in
433            let exp_named_subst' =
434             substaux_in_exp_named_subst params exp_named_subst m 
435            in
436             C.MutInd (uri,i,exp_named_subst')
437        | C.MutConstruct (uri,i,j,exp_named_subst) ->
438           let params =
439             let o,_ = 
440               CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
441             in
442            (match o with
443                C.Constant _ -> raise ReferenceToConstant
444              | C.Variable _ -> raise ReferenceToVariable
445              | C.CurrentProof _ -> raise ReferenceToCurrentProof
446              | C.InductiveDefinition (_,params,_,_) -> params
447            )
448           in
449            let exp_named_subst' =
450             substaux_in_exp_named_subst params exp_named_subst m 
451            in
452             C.MutConstruct (uri,i,j,exp_named_subst')
453        | C.MutCase (sp,i,outt,t,pl) ->
454           C.MutCase (sp,i,unwind_aux m outt, unwind_aux m t,
455            List.map (unwind_aux m) pl)
456        | C.Fix (i,fl) ->
457           let len = List.length fl in
458           let substitutedfl =
459            List.map
460             (fun (name,i,ty,bo) ->
461               (name, i, unwind_aux m ty, unwind_aux (m+len) bo))
462              fl
463           in
464            C.Fix (i, substitutedfl)
465        | C.CoFix (i,fl) ->
466           let len = List.length fl in
467           let substitutedfl =
468            List.map
469             (fun (name,ty,bo) -> (name, unwind_aux m ty, unwind_aux (m+len) bo))
470              fl
471           in
472            C.CoFix (i, substitutedfl)
473      and substaux_in_exp_named_subst params exp_named_subst' m  =
474   (*CSC: Idea di Andrea di ordinare compatibilmente con l'ordine dei params
475       let ens' =
476        List.map (function (uri,t) -> uri, unwind_aux m t) exp_named_subst' @
477   (*CSC: qui liftiamo tutti gli ens anche se magari me ne servono la meta'!!! *)
478         List.map (function (uri,t) -> uri, CicSubstitution.lift m t) ens
479       in
480       let rec filter_and_lift =
481        function
482           [] -> []
483         | uri::tl ->
484            let r = filter_and_lift tl in
485             (try
486               (uri,(List.assq uri ens'))::r
487              with
488               Not_found -> r
489             )
490       in
491        filter_and_lift params
492   *)
493   
494   (*CSC: invece di concatenare sarebbe meglio rispettare l'ordine dei params *)
495   (*CSC: e' vero???? una veloce prova non sembra confermare la teoria        *)
496   
497   (*CSC: codice copiato e modificato dalla cicSubstitution.subst_vars *)
498   (*CSC: codice altamente inefficiente *)
499       let rec filter_and_lift already_instantiated =
500        function
501           [] -> []
502         | (uri,t)::tl when
503             List.for_all
504              (function (uri',_)-> not (UriManager.eq uri uri')) exp_named_subst'
505             &&
506              not (List.mem uri already_instantiated)
507             &&
508              List.mem uri params
509            ->
510             (uri,CicSubstitution.lift m (RS.from_ens_for_unwind ~unwind t)) ::
511              (filter_and_lift (uri::already_instantiated) tl)
512         | _::tl -> filter_and_lift already_instantiated tl
513 (*
514         | (uri,_)::tl ->
515 debug_print (lazy ("---- SKIPPO " ^ UriManager.string_of_uri uri)) ;
516 if List.for_all (function (uri',_) -> not (UriManager.eq uri uri'))
517 exp_named_subst' then debug_print (lazy "---- OK1") ;
518 debug_print (lazy ("++++ uri " ^ UriManager.string_of_uri uri ^ " not in " ^ String.concat " ; " (List.map UriManager.string_of_uri params))) ;
519 if List.mem uri params then debug_print (lazy "---- OK2") ;
520         filter_and_lift tl
521 *)
522       in
523        List.map (function (uri,t) -> uri, unwind_aux m t) exp_named_subst' @
524         (filter_and_lift [] (List.rev ens))
525      in
526       unwind_aux m t          
527   
528   and unwind (k,e,ens,t,s) =
529    let t' = unwind' 0 k e ens t in
530     if s = [] then t' else Cic.Appl (t'::(RS.from_stack_list_for_unwind ~unwind s))
531   ;;
532
533 (*
534   let unwind =
535    let profiler_unwind = HExtlib.profile ~enable:profile "are_convertible.unwind" in
536     fun k e ens t ->
537      profiler_unwind.HExtlib.profile (unwind k e ens) t
538   ;;
539 *)
540   
541   let reduce ~delta ?(subst = []) context : config -> config = 
542    let module C = Cic in
543    let module S = CicSubstitution in 
544    let rec reduce =
545     function
546        (k, e, _, C.Rel n, s) as config ->
547         let config' =
548          try
549           Some (RS.from_env (List.nth e (n-1)))
550          with
551           Failure _ ->
552            try
553             begin
554              match List.nth context (n - 1 - k) with
555                 None -> assert false
556               | Some (_,C.Decl _) -> None
557               | Some (_,C.Def (x,_)) -> Some (0,[],[],S.lift (n - k) x,[])
558             end
559            with
560             Failure _ -> None
561         in
562          (match config' with 
563              Some (k',e',ens',t',s') -> reduce (k',e',ens',t',s'@s)
564            | None -> config)
565      | (k, e, ens, C.Var (uri,exp_named_subst), s) as config -> 
566          if List.exists (function (uri',_) -> UriManager.eq uri' uri) ens then
567           let (k',e',ens',t',s') = RS.from_ens (List.assq uri ens) in
568            reduce (k',e',ens',t',s'@s)
569          else
570           ( let o,_ = 
571               CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
572             in
573             match o with
574               C.Constant _ -> raise ReferenceToConstant
575             | C.CurrentProof _ -> raise ReferenceToCurrentProof
576             | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
577             | C.Variable (_,None,_,_,_) -> config
578             | C.Variable (_,Some body,_,_,_) ->
579                let ens' = push_exp_named_subst k e ens exp_named_subst in
580                 reduce (0, [], ens', body, s)
581           )
582      | (k, e, ens, C.Meta (n,l), s) as config ->
583         (try 
584            let (_, term,_) = CicUtil.lookup_subst n subst in
585            reduce (k, e, ens,CicSubstitution.subst_meta l term,s)
586          with  CicUtil.Subst_not_found _ -> config)
587      | (_, _, _, C.Sort _, _)
588      | (_, _, _, C.Implicit _, _) as config -> config
589      | (k, e, ens, C.Cast (te,ty), s) ->
590         reduce (k, e, ens, te, s)
591      | (_, _, _, C.Prod _, _) as config -> config
592      | (_, _, _, C.Lambda _, []) as config -> config
593      | (k, e, ens, C.Lambda (_,_,t), p::s) ->
594          reduce (k+1, (RS.stack_to_env ~reduce ~unwind p)::e, ens, t,s)
595      | (k, e, ens, C.LetIn (_,m,t), s) ->
596         let m' = RS.compute_to_env ~reduce ~unwind k e ens m in
597          reduce (k+1, m'::e, ens, t, s)
598      | (_, _, _, C.Appl [], _) -> assert false
599      | (k, e, ens, C.Appl (he::tl), s) ->
600         let tl' =
601          List.map
602           (function t -> RS.compute_to_stack ~reduce ~unwind (k,e,ens,t,[])) tl
603         in
604          reduce (k, e, ens, he, (List.append tl') s)
605      | (_, _, _, C.Const _, _) as config when delta=false-> config
606      | (k, e, ens, C.Const (uri,exp_named_subst), s) as config ->
607         (let o,_ = 
608            CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
609          in
610           match o with
611             C.Constant (_,Some body,_,_,_) ->
612              let ens' = push_exp_named_subst k e ens exp_named_subst in
613               (* constants are closed *)
614               reduce (0, [], ens', body, s) 
615           | C.Constant (_,None,_,_,_) -> config
616           | C.Variable _ -> raise ReferenceToVariable
617           | C.CurrentProof (_,_,body,_,_,_) ->
618              let ens' = push_exp_named_subst k e ens exp_named_subst in
619               (* constants are closed *)
620               reduce (0, [], ens', body, s)
621           | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
622         )
623      | (_, _, _, C.MutInd _, _)
624      | (_, _, _, C.MutConstruct _, _) as config -> config 
625      | (k, e, ens, C.MutCase (mutind,i,outty,term,pl),s) as config ->
626         let decofix =
627          function
628             (k, e, ens, C.CoFix (i,fl), s) ->
629              let (_,_,body) = List.nth fl i in
630               let body' =
631                let counter = ref (List.length fl) in
632                 List.fold_right
633                  (fun _ -> decr counter ; S.subst (C.CoFix (!counter,fl)))
634                  fl
635                  body
636               in
637                reduce (k,e,ens,body',s)
638           | config -> config
639         in
640          (match decofix (reduce (k,e,ens,term,[])) with
641              (k', e', ens', C.MutConstruct (_,_,j,_), []) ->
642               reduce (k, e, ens, (List.nth pl (j-1)), [])
643            | (k', e', ens', C.MutConstruct (_,_,j,_), s') ->
644               let (arity, r) =
645                 let o,_ = 
646                   CicEnvironment.get_cooked_obj CicUniv.empty_ugraph mutind 
647                 in
648                   match o with
649                       C.InductiveDefinition (s,ingredients,r,_) ->
650                         let (_,_,arity,_) = List.nth s i in
651                           (arity,r)
652                     | _ -> raise WrongUriToInductiveDefinition
653               in
654                let ts =
655                 let num_to_eat = r in
656                  let rec eat_first =
657                   function
658                      (0,l) -> l
659                    | (n,he::s) when n > 0 -> eat_first (n - 1, s)
660                    | _ -> raise (Impossible 5)
661                  in
662                   eat_first (num_to_eat,s')
663                in
664                 reduce (k, e, ens, (List.nth pl (j-1)), ts@s)
665            | (_, _, _, C.Cast _, _)
666            | (_, _, _, C.Implicit _, _) ->
667               raise (Impossible 2) (* we don't trust our whd ;-) *)
668            | config' ->
669               (*CSC: here I am unwinding the configuration and for sure I
670                 will do it twice; to avoid this unwinding I should push the
671                 "match [] with _" continuation on the stack;
672                 another possibility is to just return the original configuration,
673                 partially undoing the weak-head computation *)
674               (*this code is uncorrect since term' lives in e' <> e
675               let term' = unwind config' in
676                (k, e, ens, C.MutCase (mutind,i,outty,term',pl),s)
677               *)
678               config)
679      | (k, e, ens, C.Fix (i,fl), s) as config ->
680         let (_,recindex,_,body) = List.nth fl i in
681          let recparam =
682           try
683            Some (RS.from_stack (List.nth s recindex))
684           with
685            _ -> None
686          in
687           (match recparam with
688               Some recparam ->
689                (match reduce recparam with
690                    (_,_,_,C.MutConstruct _,_) as config ->
691                     let leng = List.length fl in
692                     let new_env =
693                      let counter = ref 0 in
694                      let rec build_env e =
695                       if !counter = leng then e
696                       else
697                        (incr counter ;
698                         build_env
699                          ((RS.to_env (k,e,ens,C.Fix (!counter -1, fl),[]))::e))
700                      in
701                       build_env e
702                     in
703                     let rec replace i s t =
704                      match i,s with
705                         0,_::tl -> t::tl
706                       | n,he::tl -> he::(replace (n - 1) tl t)
707                       | _,_ -> assert false in
708                     let new_s =
709                      replace recindex s (RS.compute_to_stack ~reduce ~unwind config)
710                     in
711                      reduce (k+leng, new_env, ens, body, new_s)
712                  | _ -> config)
713             | None -> config
714           )
715      | (_,_,_,C.CoFix _,_) as config -> config
716    and push_exp_named_subst k e ens =
717     function
718        [] -> ens
719      | (uri,t)::tl ->
720          push_exp_named_subst k e ((uri,RS.to_ens (k,e,ens,t,[]))::ens) tl
721    in
722     reduce
723   ;;
724
725   let whd ?(delta=true) ?(subst=[]) context t = 
726    unwind (reduce ~delta ~subst context (0, [], [], t, []))
727   ;;
728
729  end
730 ;;
731
732
733 (* ROTTO = rompe l'unificazione poiche' riduce gli argomenti di un'applicazione
734            senza ridurre la testa
735 module R = Reduction CallByNameStrategy;; OK 56.368s
736 module R = Reduction CallByValueStrategy;; ROTTO
737 module R = Reduction CallByValueStrategyByNameOnConstants;; ROTTO
738 module R = Reduction LazyCallByValueStrategy;; ROTTO
739 module R = Reduction LazyCallByValueStrategyByNameOnConstants;; ROTTO
740 module R = Reduction LazyCallByNameStrategy;; OK 0m56.398s
741 module R = Reduction
742  LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns;;
743  OK 59.058s
744 module R = Reduction ClosuresOnStackByValueFromEnvOrEnsStrategy;; OK 58.583s
745 module R = Reduction
746  ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy;; OK 58.094s
747 module R = Reduction(ClosuresOnStackByValueFromEnvOrEnsStrategy);; OK 58.127s
748 *)
749 module R = Reduction(CallByValueByNameForUnwind);;
750 (*module R = Reduction(ClosuresOnStackByValueFromEnvOrEnsStrategy);;*)
751 module U = UriManager;;
752
753 let whd = R.whd
754
755 (*
756 let whd =
757  let profiler_whd = HExtlib.profile ~enable:profile "are_convertible.whd" in
758   fun ?(delta=true) ?(subst=[]) context t ->
759    profiler_whd.HExtlib.profile (whd ~delta ~subst context) t
760 *)
761
762   (* mimic ocaml (<< 3.08) "=" behaviour. Tests physical equality first then
763     * fallbacks to structural equality *)
764 let (===) x y =
765   Pervasives.compare x y = 0
766
767 (* t1, t2 must be well-typed *)
768 let are_convertible whd ?(subst=[]) ?(metasenv=[])  =
769  let rec aux test_equality_only context t1 t2 ugraph =
770   let aux2 test_equality_only t1 t2 ugraph =
771
772    (* this trivial euristic cuts down the total time of about five times ;-) *)
773    (* this because most of the time t1 and t2 are "sintactically" the same   *)
774    if t1 === t2 then
775      true,ugraph
776    else
777     begin
778      let module C = Cic in
779        match (t1,t2) with
780           (C.Rel n1, C.Rel n2) -> (n1 = n2),ugraph
781         | (C.Var (uri1,exp_named_subst1), C.Var (uri2,exp_named_subst2)) ->
782             if U.eq uri1 uri2 then
783              (try
784                List.fold_right2
785                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
786                   let b',ugraph' = aux test_equality_only context x y ugraph in
787                   (U.eq uri1 uri2 && b' && b),ugraph'
788                 ) exp_named_subst1 exp_named_subst2 (true,ugraph) 
789               with
790                Invalid_argument _ -> false,ugraph
791              )
792             else
793               false,ugraph
794         | (C.Meta (n1,l1), C.Meta (n2,l2)) ->
795             if n1 = n2 then
796               let b2, ugraph1 = 
797                 let l1 = CicUtil.clean_up_local_context subst metasenv n1 l1 in
798                 let l2 = CicUtil.clean_up_local_context subst metasenv n2 l2 in
799                   List.fold_left2
800                     (fun (b,ugraph) t1 t2 ->
801                        if b then 
802                          match t1,t2 with
803                              None,_
804                            | _,None  -> true,ugraph
805                            | Some t1',Some t2' -> 
806                                aux test_equality_only context t1' t2' ugraph
807                        else
808                          false,ugraph
809                     ) (true,ugraph) l1 l2
810               in
811                 if b2 then true,ugraph1 else false,ugraph 
812             else
813               false,ugraph
814           (* TASSI: CONSTRAINTS *)
815         | (C.Sort (C.Type t1), C.Sort (C.Type t2)) when test_equality_only ->
816             true,(CicUniv.add_eq t2 t1 ugraph)
817           (* TASSI: CONSTRAINTS *)
818         | (C.Sort (C.Type t1), C.Sort (C.Type t2)) ->
819             true,(CicUniv.add_ge t2 t1 ugraph)
820           (* TASSI: CONSTRAINTS *)
821         | (C.Sort s1, C.Sort (C.Type _)) -> (not test_equality_only),ugraph
822           (* TASSI: CONSTRAINTS *)
823         | (C.Sort s1, C.Sort s2) -> (s1 = s2),ugraph
824         | (C.Prod (name1,s1,t1), C.Prod(_,s2,t2)) ->
825             let b',ugraph' = aux true context s1 s2 ugraph in
826             if b' then 
827               aux test_equality_only ((Some (name1, (C.Decl s1)))::context) 
828                 t1 t2 ugraph'
829             else
830               false,ugraph
831         | (C.Lambda (name1,s1,t1), C.Lambda(_,s2,t2)) ->
832            let b',ugraph' = aux test_equality_only context s1 s2 ugraph in
833            if b' then
834              aux test_equality_only ((Some (name1, (C.Decl s1)))::context) 
835                t1 t2 ugraph'
836            else
837              false,ugraph
838         | (C.LetIn (name1,s1,t1), C.LetIn(_,s2,t2)) ->
839            let b',ugraph' = aux test_equality_only context s1 s2 ugraph in
840            if b' then
841             aux test_equality_only
842              ((Some (name1, (C.Def (s1,None))))::context) t1 t2 ugraph'
843            else
844              false,ugraph
845         | (C.Appl l1, C.Appl l2) ->
846            (try
847              List.fold_right2
848                (fun  x y (b,ugraph) -> 
849                  if b then
850                    aux test_equality_only context x y ugraph
851                  else
852                    false,ugraph) l1 l2 (true,ugraph)
853             with
854              Invalid_argument _ -> false,ugraph
855            )
856         | (C.Const (uri1,exp_named_subst1), C.Const (uri2,exp_named_subst2)) ->
857             let b' = U.eq uri1 uri2 in
858             if b' then
859              (try
860                List.fold_right2
861                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
862                   if b && U.eq uri1 uri2 then
863                     aux test_equality_only context x y ugraph 
864                   else
865                     false,ugraph
866                 ) exp_named_subst1 exp_named_subst2 (true,ugraph)
867               with
868                Invalid_argument _ -> false,ugraph
869              )
870             else
871               false,ugraph
872         | (C.MutInd (uri1,i1,exp_named_subst1),
873            C.MutInd (uri2,i2,exp_named_subst2)
874           ) ->
875             let b' = U.eq uri1 uri2 && i1 = i2 in
876             if b' then
877              (try
878                List.fold_right2
879                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
880                   if b && U.eq uri1 uri2 then
881                     aux test_equality_only context x y ugraph
882                   else
883                    false,ugraph
884                 ) exp_named_subst1 exp_named_subst2 (true,ugraph)
885               with
886                Invalid_argument _ -> false,ugraph
887              )
888             else 
889               false,ugraph
890         | (C.MutConstruct (uri1,i1,j1,exp_named_subst1),
891            C.MutConstruct (uri2,i2,j2,exp_named_subst2)
892           ) ->
893             let b' = U.eq uri1 uri2 && i1 = i2 && j1 = j2 in
894             if b' then
895              (try
896                List.fold_right2
897                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
898                   if b && U.eq uri1 uri2 then
899                     aux test_equality_only context x y ugraph
900                   else
901                     false,ugraph
902                 ) exp_named_subst1 exp_named_subst2 (true,ugraph)
903               with
904                Invalid_argument _ -> false,ugraph
905              )
906             else
907               false,ugraph
908         | (C.MutCase (uri1,i1,outtype1,term1,pl1),
909            C.MutCase (uri2,i2,outtype2,term2,pl2)) -> 
910             let b' = U.eq uri1 uri2 && i1 = i2 in
911             if b' then
912              let b'',ugraph''=aux test_equality_only context 
913                  outtype1 outtype2 ugraph in
914              if b'' then 
915                let b''',ugraph'''= aux test_equality_only context 
916                    term1 term2 ugraph'' in
917                List.fold_right2
918                  (fun x y (b,ugraph) -> 
919                    if b then
920                      aux test_equality_only context x y ugraph 
921                    else 
922                      false,ugraph)
923                  pl1 pl2 (b''',ugraph''')
924              else
925                false,ugraph
926             else
927               false,ugraph
928         | (C.Fix (i1,fl1), C.Fix (i2,fl2)) ->
929             let tys =
930               List.map (function (n,_,ty,_) -> Some (C.Name n,(C.Decl ty))) fl1
931             in
932             if i1 = i2 then
933              List.fold_right2
934               (fun (_,recindex1,ty1,bo1) (_,recindex2,ty2,bo2) (b,ugraph) ->
935                 if b && recindex1 = recindex2 then
936                   let b',ugraph' = aux test_equality_only context ty1 ty2 
937                       ugraph in
938                   if b' then
939                     aux test_equality_only (tys@context) bo1 bo2 ugraph'
940                   else
941                     false,ugraph
942                 else
943                   false,ugraph)
944              fl1 fl2 (true,ugraph)
945             else
946               false,ugraph
947         | (C.CoFix (i1,fl1), C.CoFix (i2,fl2)) ->
948            let tys =
949             List.map (function (n,ty,_) -> Some (C.Name n,(C.Decl ty))) fl1
950            in
951             if i1 = i2 then
952               List.fold_right2
953               (fun (_,ty1,bo1) (_,ty2,bo2) (b,ugraph) ->
954                 if b then
955                   let b',ugraph' = aux test_equality_only context ty1 ty2 
956                       ugraph in
957                   if b' then
958                     aux test_equality_only (tys@context) bo1 bo2 ugraph'
959                   else
960                     false,ugraph
961                 else
962                   false,ugraph)
963              fl1 fl2 (true,ugraph)
964             else
965               false,ugraph
966         | (C.Cast _, _) | (_, C.Cast _)
967         | (C.Implicit _, _) | (_, C.Implicit _) -> assert false
968         | (_,_) -> false,ugraph
969     end
970   in
971    debug t1 [t2] "PREWHD";
972    let t1' = whd ?delta:(Some true) ?subst:(Some subst) context t1 in
973    let t2' = whd ?delta:(Some true) ?subst:(Some subst) context t2 in
974     debug t1' [t2'] "POSTWHD";
975     aux2 test_equality_only t1' t2' ugraph
976  in
977   aux false (*c t1 t2 ugraph *)
978 ;;
979
980 (* DEBUGGING ONLY
981 let whd ?(delta=true) ?(subst=[]) context t = 
982  let res = whd ~delta ~subst context t in
983  let rescsc = CicReductionNaif.whd ~delta ~subst context t in
984   if not (fst (are_convertible CicReductionNaif.whd ~subst context res rescsc CicUniv.empty_ugraph)) then
985    begin
986     debug_print (lazy ("PRIMA: " ^ CicPp.ppterm t)) ;
987     flush stderr ;
988     debug_print (lazy ("DOPO: " ^ CicPp.ppterm res)) ;
989     flush stderr ;
990     debug_print (lazy ("CSC: " ^ CicPp.ppterm rescsc)) ;
991     flush stderr ;
992 fdebug := 0 ;
993 let _ =  are_convertible CicReductionNaif.whd ~subst context res rescsc CicUniv.empty_ugraph in
994     assert false ;
995    end
996   else 
997    res
998 ;;
999 *)
1000
1001 let are_convertible = are_convertible whd
1002
1003 let whd = R.whd
1004
1005 (*
1006 let profiler_other_whd = HExtlib.profile ~enable:profile "~are_convertible.whd"
1007 let whd ?(delta=true) ?(subst=[]) context t = 
1008  let foo () =
1009   whd ~delta ~subst context t
1010  in
1011   profiler_other_whd.HExtlib.profile foo ()
1012 *)
1013
1014 let rec normalize ?(delta=true) ?(subst=[]) ctx term =
1015   let module C = Cic in
1016   let t = whd ~delta ~subst ctx term in
1017   let aux = normalize ~delta ~subst in
1018   let decl name t = Some (name, C.Decl t) in
1019   match t with
1020   | C.Rel n -> t
1021   | C.Var (uri,exp_named_subst) ->
1022       C.Var (uri, List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1023   | C.Meta (i,l) -> 
1024       C.Meta (i,List.map (function Some t -> Some (aux ctx t) | None -> None) l)
1025   | C.Sort _ -> t
1026   | C.Implicit _ -> t
1027   | C.Cast (te,ty) -> C.Cast (aux ctx te, aux ctx ty)
1028   | C.Prod (n,s,t) -> 
1029       let s' = aux ctx s in
1030       C.Prod (n, s', aux ((decl n s')::ctx) t)
1031   | C.Lambda (n,s,t) -> 
1032       let s' = aux ctx s in
1033       C.Lambda (n, s', aux ((decl n s')::ctx) t)
1034   | C.LetIn (n,s,t) ->
1035       (* the term is already in weak head normal form *)
1036       assert false
1037   | C.Appl (h::l) -> C.Appl (h::(List.map (aux ctx) l))
1038   | C.Appl [] -> assert false
1039   | C.Const (uri,exp_named_subst) ->
1040       C.Const (uri, List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1041   | C.MutInd (uri,typeno,exp_named_subst) ->
1042       C.MutInd (uri,typeno, List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1043   | C.MutConstruct (uri,typeno,consno,exp_named_subst) ->
1044       C.MutConstruct (uri, typeno, consno, 
1045         List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1046   | C.MutCase (sp,i,outt,t,pl) ->
1047       C.MutCase (sp,i, aux ctx outt, aux ctx t, List.map (aux ctx) pl)
1048 (*CSC: to be completed, I suppose *)
1049   | C.Fix _ -> t 
1050   | C.CoFix _ -> t
1051
1052 let normalize ?delta ?subst ctx term =  
1053 (*  prerr_endline ("NORMALIZE:" ^ CicPp.ppterm term); *)
1054   let t = normalize ?delta ?subst ctx term in
1055 (*  prerr_endline ("NORMALIZED:" ^ CicPp.ppterm t); *)
1056   t
1057   
1058   
1059 (* performs an head beta/cast reduction *)
1060 let rec head_beta_reduce =
1061  function
1062     (Cic.Appl (Cic.Lambda (_,_,t)::he'::tl')) ->
1063       let he'' = CicSubstitution.subst he' t in
1064        if tl' = [] then
1065         he''
1066        else
1067         let he''' =
1068          match he'' with
1069             Cic.Appl l -> Cic.Appl (l@tl')
1070           | _ -> Cic.Appl (he''::tl')
1071         in
1072          head_beta_reduce he'''
1073   | Cic.Cast (te,_) -> head_beta_reduce te
1074   | t -> t