]> matita.cs.unibo.it Git - helm.git/blob - helm/ocaml/cic_proof_checking/cicReduction.ml
(Partial commit)
[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   (* CSC: Old Dead Code 
606      | (k, e, ens, C.Appl ((C.Lambda _ as he)::tl), s) 
607      | (k, e, ens, C.Appl ((C.Const _ as he)::tl), s)  
608      | (k, e, ens, C.Appl ((C.MutCase _ as he)::tl), s) 
609      | (k, e, ens, C.Appl ((C.Fix _ as he)::tl), s) ->
610   (* strict evaluation, but constants are NOT unfolded *)
611         let red =
612          function
613             C.Const _ as t -> unwind k e ens t
614           | t -> reduce (k,e,ens,t,[])
615         in
616          let tl' = List.map red tl in
617           reduce (k, e, ens, he , List.append tl' s)
618      | (k, e, ens, C.Appl l, s) ->
619          C.Appl (List.append (List.map (unwind k e ens) l) s)
620   *)
621      | (_, _, _, C.Const _, _) as config when delta=false-> config
622      | (k, e, ens, C.Const (uri,exp_named_subst), s) as config ->
623         (let o,_ = 
624            CicEnvironment.get_cooked_obj CicUniv.empty_ugraph uri
625          in
626           match o with
627             C.Constant (_,Some body,_,_,_) ->
628              let ens' = push_exp_named_subst k e ens exp_named_subst in
629               (* constants are closed *)
630               reduce (0, [], ens', body, s) 
631           | C.Constant (_,None,_,_,_) -> config
632           | C.Variable _ -> raise ReferenceToVariable
633           | C.CurrentProof (_,_,body,_,_,_) ->
634              let ens' = push_exp_named_subst k e ens exp_named_subst in
635               (* constants are closed *)
636               reduce (0, [], ens', body, s)
637           | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
638         )
639      | (_, _, _, C.MutInd _, _)
640      | (_, _, _, C.MutConstruct _, _) as config -> config 
641      | (k, e, ens, C.MutCase (mutind,i,outty,term,pl),s) as config ->
642         let decofix =
643          function
644             (k, e, ens, C.CoFix (i,fl), s) ->
645              let (_,_,body) = List.nth fl i in
646               let body' =
647                let counter = ref (List.length fl) in
648                 List.fold_right
649                  (fun _ -> decr counter ; S.subst (C.CoFix (!counter,fl)))
650                  fl
651                  body
652               in
653                reduce (k,e,ens,body',s)
654           | config -> config
655         in
656          (match decofix (reduce (k,e,ens,term,[])) with
657              (k', e', ens', C.MutConstruct (_,_,j,_), []) ->
658               reduce (k, e, ens, (List.nth pl (j-1)), [])
659            | (k', e', ens', C.MutConstruct (_,_,j,_), s') ->
660               let (arity, r) =
661                 let o,_ = 
662                   CicEnvironment.get_cooked_obj CicUniv.empty_ugraph mutind 
663                 in
664                   match o with
665                       C.InductiveDefinition (s,ingredients,r,_) ->
666                         let (_,_,arity,_) = List.nth s i in
667                           (arity,r)
668                     | _ -> raise WrongUriToInductiveDefinition
669               in
670                let ts =
671                 let num_to_eat = r in
672                  let rec eat_first =
673                   function
674                      (0,l) -> l
675                    | (n,he::s) when n > 0 -> eat_first (n - 1, s)
676                    | _ -> raise (Impossible 5)
677                  in
678                   eat_first (num_to_eat,s')
679                in
680                 reduce (k, e, ens, (List.nth pl (j-1)), ts@s)
681            | (_, _, _, C.Cast _, _)
682            | (_, _, _, C.Implicit _, _) ->
683               raise (Impossible 2) (* we don't trust our whd ;-) *)
684            | config' ->
685               (*CSC: here I am unwinding the configuration and for sure I
686                 will do it twice; to avoid this unwinding I should push the
687                 "match [] with _" continuation on the stack;
688                 another possibility is to just return the original configuration,
689                 partially undoing the weak-head computation *)
690               (*this code is uncorrect since term' lives in e' <> e
691               let term' = unwind config' in
692                (k, e, ens, C.MutCase (mutind,i,outty,term',pl),s)
693               *)
694               config)
695      | (k, e, ens, C.Fix (i,fl), s) as config ->
696         let (_,recindex,_,body) = List.nth fl i in
697          let recparam =
698           try
699            Some (RS.from_stack (List.nth s recindex))
700           with
701            _ -> None
702          in
703           (match recparam with
704               Some recparam ->
705                (match reduce recparam with
706                    (* match recparam with *) 
707                    (_,_,_,C.MutConstruct _,_) as config ->
708                     (* OLD 
709                     let body' =
710                      let counter = ref (List.length fl) in
711                       List.fold_right
712                        (fun _ -> decr counter ; S.subst (C.Fix (!counter,fl)))
713                        fl
714                        body
715                     in 
716                      reduce (k, e, ens, body', s) *)
717                     (* NEW *)
718                     let leng = List.length fl in
719                     let new_env =
720                      let counter = ref 0 in
721                      let rec build_env e =
722                       if !counter = leng then e
723                       else
724                        (incr counter ;
725                         build_env
726                          ((RS.to_env (k,e,ens,C.Fix (!counter -1, fl),[]))::e))
727                      in
728                       build_env e
729                     in
730                     let rec replace i s t =
731                      match i,s with
732                         0,_::tl -> t::tl
733                       | n,he::tl -> he::(replace (n - 1) tl t)
734                       | _,_ -> assert false in
735                     let new_s =
736                      replace recindex s (RS.compute_to_stack ~reduce ~unwind config)
737                     in
738                      reduce (k+leng, new_env, ens, body, new_s)
739                  | _ -> config)
740             | None -> config
741           )
742      | (_,_,_,C.CoFix _,_) as config -> config
743    and push_exp_named_subst k e ens =
744     function
745        [] -> ens
746      | (uri,t)::tl ->
747          push_exp_named_subst k e ((uri,RS.to_ens (k,e,ens,t,[]))::ens) tl
748    in
749     reduce
750   ;;
751   (*
752   let rec whd context t = 
753     try 
754       reduce context (0, [], [], t, [])
755     with Not_found -> 
756       debug_print (lazy (CicPp.ppterm t)) ; 
757       raise Not_found
758   ;;
759   *)
760
761   let whd ?(delta=true) ?(subst=[]) context t = 
762    unwind (reduce ~delta ~subst context (0, [], [], t, []))
763   ;;
764
765   
766  end
767 ;;
768
769
770 (* ROTTO = rompe l'unificazione poiche' riduce gli argomenti di un'applicazione
771            senza ridurre la testa
772 module R = Reduction CallByNameStrategy;; OK 56.368s
773 module R = Reduction CallByValueStrategy;; ROTTO
774 module R = Reduction CallByValueStrategyByNameOnConstants;; ROTTO
775 module R = Reduction LazyCallByValueStrategy;; ROTTO
776 module R = Reduction LazyCallByValueStrategyByNameOnConstants;; ROTTO
777 module R = Reduction LazyCallByNameStrategy;; OK 0m56.398s
778 module R = Reduction
779  LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns;;
780  OK 59.058s
781 module R = Reduction ClosuresOnStackByValueFromEnvOrEnsStrategy;; OK 58.583s
782 module R = Reduction
783  ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy;; OK 58.094s
784 module R = Reduction(ClosuresOnStackByValueFromEnvOrEnsStrategy);; OK 58.127s
785 *)
786 (*module R = Reduction(CallByValueByNameForUnwind);; *)
787 module R = Reduction(ClosuresOnStackByValueFromEnvOrEnsStrategy);;
788 module U = UriManager;;
789
790 let whd = R.whd
791
792 (*
793 let whd =
794  let profiler_whd = HExtlib.profile ~enable:profile "are_convertible.whd" in
795   fun ?(delta=true) ?(subst=[]) context t ->
796    profiler_whd.HExtlib.profile (whd ~delta ~subst context) t
797 *)
798
799   (* mimic ocaml (<< 3.08) "=" behaviour. Tests physical equality first then
800     * fallbacks to structural equality *)
801 let (===) x y =
802   Pervasives.compare x y = 0
803
804 (* t1, t2 must be well-typed *)
805 let are_convertible whd ?(subst=[]) ?(metasenv=[])  =
806  let rec aux test_equality_only context t1 t2 ugraph =
807   let aux2 test_equality_only t1 t2 ugraph =
808
809    (* this trivial euristic cuts down the total time of about five times ;-) *)
810    (* this because most of the time t1 and t2 are "sintactically" the same   *)
811    if t1 === t2 then
812      true,ugraph
813    else
814     begin
815      let module C = Cic in
816        match (t1,t2) with
817           (C.Rel n1, C.Rel n2) -> (n1 = n2),ugraph
818         | (C.Var (uri1,exp_named_subst1), C.Var (uri2,exp_named_subst2)) ->
819             if U.eq uri1 uri2 then
820              (try
821                List.fold_right2
822                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
823                   let b',ugraph' = aux test_equality_only context x y ugraph in
824                   (U.eq uri1 uri2 && b' && b),ugraph'
825                 ) exp_named_subst1 exp_named_subst2 (true,ugraph) 
826               with
827                Invalid_argument _ -> false,ugraph
828              )
829             else
830               false,ugraph
831         | (C.Meta (n1,l1), C.Meta (n2,l2)) ->
832             if n1 = n2 then
833               let b2, ugraph1 = 
834                 let l1 = CicUtil.clean_up_local_context subst metasenv n1 l1 in
835                 let l2 = CicUtil.clean_up_local_context subst metasenv n2 l2 in
836                   List.fold_left2
837                     (fun (b,ugraph) t1 t2 ->
838                        if b then 
839                          match t1,t2 with
840                              None,_
841                            | _,None  -> true,ugraph
842                            | Some t1',Some t2' -> 
843                                aux test_equality_only context t1' t2' ugraph
844                        else
845                          false,ugraph
846                     ) (true,ugraph) l1 l2
847               in
848                 if b2 then true,ugraph1 else false,ugraph 
849             else
850               false,ugraph
851           (* TASSI: CONSTRAINTS *)
852         | (C.Sort (C.Type t1), C.Sort (C.Type t2)) when test_equality_only ->
853             true,(CicUniv.add_eq t2 t1 ugraph)
854           (* TASSI: CONSTRAINTS *)
855         | (C.Sort (C.Type t1), C.Sort (C.Type t2)) ->
856             true,(CicUniv.add_ge t2 t1 ugraph)
857           (* TASSI: CONSTRAINTS *)
858         | (C.Sort s1, C.Sort (C.Type _)) -> (not test_equality_only),ugraph
859           (* TASSI: CONSTRAINTS *)
860         | (C.Sort s1, C.Sort s2) -> (s1 = s2),ugraph
861         | (C.Prod (name1,s1,t1), C.Prod(_,s2,t2)) ->
862             let b',ugraph' = aux true context s1 s2 ugraph in
863             if b' then 
864               aux test_equality_only ((Some (name1, (C.Decl s1)))::context) 
865                 t1 t2 ugraph'
866             else
867               false,ugraph
868         | (C.Lambda (name1,s1,t1), C.Lambda(_,s2,t2)) ->
869            let b',ugraph' = aux test_equality_only context s1 s2 ugraph in
870            if b' then
871              aux test_equality_only ((Some (name1, (C.Decl s1)))::context) 
872                t1 t2 ugraph'
873            else
874              false,ugraph
875         | (C.LetIn (name1,s1,t1), C.LetIn(_,s2,t2)) ->
876            let b',ugraph' = aux test_equality_only context s1 s2 ugraph in
877            if b' then
878             aux test_equality_only
879              ((Some (name1, (C.Def (s1,None))))::context) t1 t2 ugraph'
880            else
881              false,ugraph
882         | (C.Appl l1, C.Appl l2) ->
883            (try
884              List.fold_right2
885                (fun  x y (b,ugraph) -> 
886                  if b then
887                    aux test_equality_only context x y ugraph
888                  else
889                    false,ugraph) l1 l2 (true,ugraph)
890             with
891              Invalid_argument _ -> false,ugraph
892            )
893         | (C.Const (uri1,exp_named_subst1), C.Const (uri2,exp_named_subst2)) ->
894             let b' = U.eq uri1 uri2 in
895             if b' then
896              (try
897                List.fold_right2
898                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
899                   if b && U.eq uri1 uri2 then
900                     aux test_equality_only context x y ugraph 
901                   else
902                     false,ugraph
903                 ) exp_named_subst1 exp_named_subst2 (true,ugraph)
904               with
905                Invalid_argument _ -> false,ugraph
906              )
907             else
908               false,ugraph
909         | (C.MutInd (uri1,i1,exp_named_subst1),
910            C.MutInd (uri2,i2,exp_named_subst2)
911           ) ->
912             let b' = U.eq uri1 uri2 && i1 = i2 in
913             if b' then
914              (try
915                List.fold_right2
916                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
917                   if b && U.eq uri1 uri2 then
918                     aux test_equality_only context x y ugraph
919                   else
920                    false,ugraph
921                 ) exp_named_subst1 exp_named_subst2 (true,ugraph)
922               with
923                Invalid_argument _ -> false,ugraph
924              )
925             else 
926               false,ugraph
927         | (C.MutConstruct (uri1,i1,j1,exp_named_subst1),
928            C.MutConstruct (uri2,i2,j2,exp_named_subst2)
929           ) ->
930             let b' = U.eq uri1 uri2 && i1 = i2 && j1 = j2 in
931             if b' then
932              (try
933                List.fold_right2
934                 (fun (uri1,x) (uri2,y) (b,ugraph) ->
935                   if b && U.eq uri1 uri2 then
936                     aux test_equality_only context x y ugraph
937                   else
938                     false,ugraph
939                 ) exp_named_subst1 exp_named_subst2 (true,ugraph)
940               with
941                Invalid_argument _ -> false,ugraph
942              )
943             else
944               false,ugraph
945         | (C.MutCase (uri1,i1,outtype1,term1,pl1),
946            C.MutCase (uri2,i2,outtype2,term2,pl2)) -> 
947             let b' = U.eq uri1 uri2 && i1 = i2 in
948             if b' then
949              let b'',ugraph''=aux test_equality_only context 
950                  outtype1 outtype2 ugraph in
951              if b'' then 
952                let b''',ugraph'''= aux test_equality_only context 
953                    term1 term2 ugraph'' in
954                List.fold_right2
955                  (fun x y (b,ugraph) -> 
956                    if b then
957                      aux test_equality_only context x y ugraph 
958                    else 
959                      false,ugraph)
960                  pl1 pl2 (b''',ugraph''')
961              else
962                false,ugraph
963             else
964               false,ugraph
965         | (C.Fix (i1,fl1), C.Fix (i2,fl2)) ->
966             let tys =
967               List.map (function (n,_,ty,_) -> Some (C.Name n,(C.Decl ty))) fl1
968             in
969             if i1 = i2 then
970              List.fold_right2
971               (fun (_,recindex1,ty1,bo1) (_,recindex2,ty2,bo2) (b,ugraph) ->
972                 if b && recindex1 = recindex2 then
973                   let b',ugraph' = aux test_equality_only context ty1 ty2 
974                       ugraph in
975                   if b' then
976                     aux test_equality_only (tys@context) bo1 bo2 ugraph'
977                   else
978                     false,ugraph
979                 else
980                   false,ugraph)
981              fl1 fl2 (true,ugraph)
982             else
983               false,ugraph
984         | (C.CoFix (i1,fl1), C.CoFix (i2,fl2)) ->
985            let tys =
986             List.map (function (n,ty,_) -> Some (C.Name n,(C.Decl ty))) fl1
987            in
988             if i1 = i2 then
989               List.fold_right2
990               (fun (_,ty1,bo1) (_,ty2,bo2) (b,ugraph) ->
991                 if b then
992                   let b',ugraph' = aux test_equality_only context ty1 ty2 
993                       ugraph in
994                   if b' then
995                     aux test_equality_only (tys@context) bo1 bo2 ugraph'
996                   else
997                     false,ugraph
998                 else
999                   false,ugraph)
1000              fl1 fl2 (true,ugraph)
1001             else
1002               false,ugraph
1003         | (C.Cast _, _) | (_, C.Cast _)
1004         | (C.Implicit _, _) | (_, C.Implicit _) -> assert false
1005         | (_,_) -> false,ugraph
1006     end
1007   in
1008     begin
1009      debug t1 [t2] "PREWHD";
1010      let t1' = whd ?delta:(Some true) ?subst:(Some subst) context t1 in
1011      let t2' = whd ?delta:(Some true) ?subst:(Some subst) context t2 in
1012       debug t1' [t2'] "POSTWHD";
1013       aux2 test_equality_only t1' t2' ugraph
1014     end
1015  in
1016   aux false (*c t1 t2 ugraph *)
1017 ;;
1018
1019 (* DEBUGGING ONLY
1020 let whd ?(delta=true) ?(subst=[]) context t = 
1021  let res = whd ~delta ~subst context t in
1022  let rescsc = CicReductionNaif.whd ~delta ~subst context t in
1023   if not (fst (are_convertible CicReductionNaif.whd ~subst context res rescsc CicUniv.empty_ugraph)) then
1024    begin
1025     debug_print (lazy ("PRIMA: " ^ CicPp.ppterm t)) ;
1026     flush stderr ;
1027     debug_print (lazy ("DOPO: " ^ CicPp.ppterm res)) ;
1028     flush stderr ;
1029     debug_print (lazy ("CSC: " ^ CicPp.ppterm rescsc)) ;
1030     flush stderr ;
1031 fdebug := 0 ;
1032 let _ =  are_convertible CicReductionNaif.whd ~subst context res rescsc CicUniv.empty_ugraph in
1033     assert false ;
1034    end
1035   else 
1036    res
1037 ;;
1038 *)
1039
1040 let are_convertible = are_convertible whd
1041
1042 let whd = R.whd
1043
1044 (*
1045 let profiler_other_whd = HExtlib.profile ~enable:profile "~are_convertible.whd"
1046 let whd ?(delta=true) ?(subst=[]) context t = 
1047  let foo () =
1048   whd ~delta ~subst context t
1049  in
1050   profiler_other_whd.HExtlib.profile foo ()
1051 *)
1052
1053 let rec normalize ?(delta=true) ?(subst=[]) ctx term =
1054   let module C = Cic in
1055   let t = whd ~delta ~subst ctx term in
1056   let aux = normalize ~delta ~subst in
1057   let decl name t = Some (name, C.Decl t) in
1058   match t with
1059   | C.Rel n -> t
1060   | C.Var (uri,exp_named_subst) ->
1061       C.Var (uri, List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1062   | C.Meta (i,l) -> 
1063       C.Meta (i,List.map (function Some t -> Some (aux ctx t) | None -> None) l)
1064   | C.Sort _ -> t
1065   | C.Implicit _ -> t
1066   | C.Cast (te,ty) -> C.Cast (aux ctx te, aux ctx ty)
1067   | C.Prod (n,s,t) -> 
1068       let s' = aux ctx s in
1069       C.Prod (n, s', aux ((decl n s')::ctx) t)
1070   | C.Lambda (n,s,t) -> 
1071       let s' = aux ctx s in
1072       C.Lambda (n, s', aux ((decl n s')::ctx) t)
1073   | C.LetIn (n,s,t) ->
1074       (* the term is already in weak head normal form *)
1075       assert false
1076   | C.Appl (h::l) -> C.Appl (h::(List.map (aux ctx) l))
1077   | C.Appl [] -> assert false
1078   | C.Const (uri,exp_named_subst) ->
1079       C.Const (uri, List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1080   | C.MutInd (uri,typeno,exp_named_subst) ->
1081       C.MutInd (uri,typeno, List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1082   | C.MutConstruct (uri,typeno,consno,exp_named_subst) ->
1083       C.MutConstruct (uri, typeno, consno, 
1084         List.map (fun (n,t) -> n,aux ctx t) exp_named_subst)
1085   | C.MutCase (sp,i,outt,t,pl) ->
1086       C.MutCase (sp,i, aux ctx outt, aux ctx t, List.map (aux ctx) pl)
1087 (*CSC: to be completed, I suppose *)
1088   | C.Fix _ -> t 
1089   | C.CoFix _ -> t
1090
1091 let normalize ?delta ?subst ctx term =  
1092 (*  prerr_endline ("NORMALIZE:" ^ CicPp.ppterm term); *)
1093   let t = normalize ?delta ?subst ctx term in
1094 (*  prerr_endline ("NORMALIZED:" ^ CicPp.ppterm t); *)
1095   t
1096   
1097   
1098 (* performs an head beta/cast reduction *)
1099 let rec head_beta_reduce =
1100  function
1101     (Cic.Appl (Cic.Lambda (_,_,t)::he'::tl')) ->
1102       let he'' = CicSubstitution.subst he' t in
1103        if tl' = [] then
1104         he''
1105        else
1106         let he''' =
1107          match he'' with
1108             Cic.Appl l -> Cic.Appl (l@tl')
1109           | _ -> Cic.Appl (he''::tl')
1110         in
1111          head_beta_reduce he'''
1112   | Cic.Cast (te,_) -> head_beta_reduce te
1113   | t -> t