]> matita.cs.unibo.it Git - helm.git/blobdiff - helm/ocaml/cic_proof_checking/cicReductionMachine.ml
- the mathql interpreter is not helm-dependent any more
[helm.git] / helm / ocaml / cic_proof_checking / cicReductionMachine.ml
index 93335625a25c0951ef4ade711069f75ef3bc19fd..30b688264776e8f2c77816fdf52e5ece0577daa5 100644 (file)
 
 exception CicReductionInternalError;;
 exception WrongUriToInductiveDefinition;;
+exception Impossible of int;;
+exception ReferenceToConstant;;
+exception ReferenceToVariable;;
+exception ReferenceToCurrentProof;;
+exception ReferenceToInductiveDefinition;;
 
 let fdebug = ref 1;;
 let debug t env s =
  let rec debug_aux t i =
   let module C = Cic in
   let module U = UriManager in
-   CicPp.ppobj (C.Variable ("DEBUG", None, t)) ^ "\n" ^ i
+   CicPp.ppobj (C.Variable ("DEBUG", None, t, [])) ^ "\n" ^ i
  in
   if !fdebug = 0 then
    begin
@@ -40,269 +45,694 @@ let debug t env s =
    end
 ;;
 
-exception Impossible of int;;
-exception ReferenceToDefinition;;
-exception ReferenceToAxiom;;
-exception ReferenceToVariable;;
-exception ReferenceToCurrentProof;;
-exception ReferenceToInductiveDefinition;;
+module type Strategy =
+ sig
+  type stack_term
+  type env_term
+  type ens_term
+  val to_stack : Cic.term -> stack_term
+  val to_stack_list : Cic.term list -> stack_term list
+  val to_env : Cic.term -> env_term
+  val to_ens : Cic.term -> ens_term
+  val from_stack :
+   unwind:
+    (int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+      Cic.term -> Cic.term) ->
+   stack_term -> Cic.term
+  val from_stack_list :
+   unwind:
+    (int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+      Cic.term -> Cic.term) ->
+   stack_term list -> Cic.term list
+  val from_env : env_term -> Cic.term
+  val from_ens : ens_term -> Cic.term
+  val stack_to_env :
+   reduce:
+    (int * env_term list * ens_term Cic.explicit_named_substitution *
+      Cic.term * stack_term list -> Cic.term) ->
+   unwind:
+    (int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+      Cic.term -> Cic.term) ->
+   stack_term -> env_term
+  val compute_to_env :
+   reduce:
+    (int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term *
+      stack_term list -> Cic.term) ->
+   unwind:
+    (int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+      Cic.term -> Cic.term) ->
+   int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+    Cic.term -> env_term
+  val compute_to_stack :
+   reduce:
+    (int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term *
+      stack_term list -> Cic.term) ->
+   unwind:
+    (int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+      Cic.term -> Cic.term) ->
+   int -> env_term list -> ens_term Cic.explicit_named_substitution ->
+    Cic.term -> stack_term
+ end
+;;
 
-type env = Cic.term list;;
-type stack = Cic.term list;;
-type config = int * env * Cic.term * stack;;
+module CallByNameStrategy =
+ struct
+  type stack_term = Cic.term
+  type env_term = Cic.term
+  type ens_term = Cic.term
+  let to_stack v = v
+  let to_stack_list l = l
+  let to_env v = v
+  let to_ens v = v
+  let from_stack ~unwind v = v
+  let from_stack_list ~unwind l = l
+  let from_env v = v
+  let from_ens v = v
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens t = unwind k e ens t
+  let compute_to_env ~reduce ~unwind k e ens t = unwind k e ens t
+ end
+;;
 
-(* k is the length of the environment e *)
-(* m is the current depth inside the term *)
-let unwind' m k e t = 
-    let module C = Cic in
-    let module S = CicSubstitution in
-    if e = [] & k = 0 then t else 
-    let rec unwind_aux m = function
-      C.Rel n as t -> if n <= m then t else
-                   let d = try Some (List.nth e (n-m-1))
-                              with _ -> None
-                   in (match d with 
-                   Some t' -> if m = 0 then t'
-                              else S.lift m t'
-                  | None -> C.Rel (n-k))
-    | C.Var _ as t  -> t
-    | C.Meta (i,l) as t -> t
-    | C.Sort _ as t -> t
-    | C.Implicit as t -> t
-    | C.Cast (te,ty) -> C.Cast (unwind_aux m te, unwind_aux m ty) (*CSC ??? *)
-    | C.Prod (n,s,t) -> C.Prod (n, unwind_aux m s, unwind_aux (m + 1) t)
-    | C.Lambda (n,s,t) -> C.Lambda (n, unwind_aux m s, unwind_aux (m + 1) t)
-    | C.LetIn (n,s,t) -> C.LetIn (n, unwind_aux m s, unwind_aux (m + 1) t)
-    | C.Appl l -> C.Appl (List.map (unwind_aux m) l)
-    | C.Const _ as t -> t
-    | C.MutInd _ as t -> t
-    | C.MutConstruct _ as t -> t
-    | C.MutCase (sp,cookingsno,i,outt,t,pl) ->
-       C.MutCase (sp,cookingsno,i,unwind_aux m outt, unwind_aux m t,
-        List.map (unwind_aux m) pl)
-    | C.Fix (i,fl) ->
-       let len = List.length fl in
-       let substitutedfl =
-        List.map
-         (fun (name,i,ty,bo) -> (name, i, unwind_aux m ty, unwind_aux (m+len) bo))
-          fl
-       in
-        C.Fix (i, substitutedfl)
-    | C.CoFix (i,fl) ->
-       let len = List.length fl in
-       let substitutedfl =
-        List.map
-         (fun (name,ty,bo) -> (name, unwind_aux m ty, unwind_aux (m+len) bo))
-          fl
-       in
-        C.CoFix (i, substitutedfl)
- in
-  unwind_aux m t          
-    ;;
+module CallByValueStrategy =
+ struct
+  type stack_term = Cic.term
+  type env_term = Cic.term
+  type ens_term = Cic.term
+  let to_stack v = v
+  let to_stack_list l = l
+  let to_env v = v
+  let to_ens v = v
+  let from_stack ~unwind v = v
+  let from_stack_list ~unwind l = l
+  let from_env v = v
+  let from_ens v = v
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens t = reduce (k,e,ens,t,[])
+  let compute_to_env ~reduce ~unwind k e ens t = reduce (k,e,ens,t,[])
+ end
+;;
+
+module CallByValueStrategyByNameOnConstants =
+ struct
+  type stack_term = Cic.term
+  type env_term = Cic.term
+  type ens_term = Cic.term
+  let to_stack v = v
+  let to_stack_list l = l
+  let to_env v = v
+  let to_ens v = v
+  let from_stack ~unwind v = v
+  let from_stack_list ~unwind l = l
+  let from_env v = v
+  let from_ens v = v
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens =
+   function
+      Cic.Const _ as t -> unwind k e ens t    
+    | t -> reduce (k,e,ens,t,[])
+  let compute_to_env ~reduce ~unwind k e ens =
+   function
+      Cic.Const _ as t -> unwind k e ens t    
+    | t -> reduce (k,e,ens,t,[])
+ end
+;;
+
+module LazyCallByValueStrategy =
+ struct
+  type stack_term = Cic.term lazy_t
+  type env_term = Cic.term lazy_t
+  type ens_term = Cic.term lazy_t
+  let to_stack v = lazy v
+  let to_stack_list l = List.map to_stack l
+  let to_env v = lazy v
+  let to_ens v = lazy v
+  let from_stack ~unwind v = Lazy.force v
+  let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
+  let from_env v = Lazy.force v
+  let from_ens v = Lazy.force v
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens t = lazy (reduce (k,e,ens,t,[]))
+  let compute_to_env ~reduce ~unwind k e ens t = lazy (reduce (k,e,ens,t,[]))
+ end
+;;
 
-let unwind =
- unwind' 0 
+module LazyCallByValueStrategyByNameOnConstants =
+ struct
+  type stack_term = Cic.term lazy_t
+  type env_term = Cic.term lazy_t
+  type ens_term = Cic.term lazy_t
+  let to_stack v = lazy v
+  let to_stack_list l = List.map to_stack l
+  let to_env v = lazy v
+  let to_ens v = lazy v
+  let from_stack ~unwind v = Lazy.force v
+  let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
+  let from_env v = Lazy.force v
+  let from_ens v = Lazy.force v
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens t =
+   lazy (
+    match t with
+       Cic.Const _ as t -> unwind k e ens t    
+     | t -> reduce (k,e,ens,t,[]))
+  let compute_to_env ~reduce ~unwind k e ens t =
+   lazy (
+    match t with
+       Cic.Const _ as t -> unwind k e ens t    
+     | t -> reduce (k,e,ens,t,[]))
+ end
 ;;
 
-let rec reduce : config -> Cic.term = 
-    let module C = Cic in
-    let module S = CicSubstitution in 
+module LazyCallByNameStrategy =
+ struct
+  type stack_term = Cic.term lazy_t
+  type env_term = Cic.term lazy_t
+  type ens_term = Cic.term lazy_t
+  let to_stack v = lazy v
+  let to_stack_list l = List.map to_stack l
+  let to_env v = lazy v
+  let to_ens v = lazy v
+  let from_stack ~unwind v = Lazy.force v
+  let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
+  let from_env v = Lazy.force v
+  let from_ens v = Lazy.force v
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens t = lazy (unwind k e ens t)
+  let compute_to_env ~reduce ~unwind k e ens t = lazy (unwind k e ens t)
+ end
+;;
+
+module
+ LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns
+=
+ struct
+  type stack_term = reduce:bool -> Cic.term
+  type env_term = reduce:bool -> Cic.term
+  type ens_term = reduce:bool -> Cic.term
+  let to_stack v =
+   let value = lazy v in
+    fun ~reduce -> Lazy.force value
+  let to_stack_list l = List.map to_stack l
+  let to_env v =
+   let value = lazy v in
+    fun ~reduce -> Lazy.force value
+  let to_ens v =
+   let value = lazy v in
+    fun ~reduce -> Lazy.force value
+  let from_stack ~unwind v = (v ~reduce:false)
+  let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
+  let from_env v = (v ~reduce:true)
+  let from_ens v = (v ~reduce:true)
+  let stack_to_env ~reduce ~unwind v = v
+  let compute_to_stack ~reduce ~unwind k e ens t =
+   let svalue =
+     lazy (
+      match t with
+         Cic.Const _ as t -> unwind k e ens t    
+       | t -> reduce (k,e,ens,t,[])
+     ) in
+   let lvalue =
+    lazy (unwind k e ens t)
+   in
+    fun ~reduce ->
+     if reduce then Lazy.force svalue else Lazy.force lvalue
+  let compute_to_env ~reduce ~unwind k e ens t =
+   let svalue =
+     lazy (
+      match t with
+         Cic.Const _ as t -> unwind k e ens t    
+       | t -> reduce (k,e,ens,t,[])
+     ) in
+   let lvalue =
+    lazy (unwind k e ens t)
+   in
+    fun ~reduce ->
+     if reduce then Lazy.force svalue else Lazy.force lvalue
+ end
+;;
+
+module ClosuresOnStackByValueFromEnvOrEnsStrategy =
+ struct
+  type stack_term =
+   int * Cic.term list * Cic.term Cic.explicit_named_substitution * Cic.term
+  type env_term = Cic.term
+  type ens_term = Cic.term
+  let to_stack v = (0,[],[],v)
+  let to_stack_list l = List.map to_stack l
+  let to_env v = v
+  let to_ens v = v
+  let from_stack ~unwind (k,e,ens,t) = unwind k e ens t
+  let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
+  let from_env v = v
+  let from_ens v = v
+  let stack_to_env ~reduce ~unwind (k,e,ens,t) = reduce (k,e,ens,t,[])
+  let compute_to_env ~reduce ~unwind k e ens t =
+   unwind k e ens t
+  let compute_to_stack ~reduce ~unwind k e ens t = (k,e,ens,t)
+ end
+;;
+
+module ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy =
+ struct
+  type stack_term =
+   int * Cic.term list * Cic.term Cic.explicit_named_substitution * Cic.term
+  type env_term = Cic.term
+  type ens_term = Cic.term
+  let to_stack v = (0,[],[],v)
+  let to_stack_list l = List.map to_stack l
+  let to_env v = v
+  let to_ens v = v
+  let from_stack ~unwind (k,e,ens,t) = unwind k e ens t
+  let from_stack_list ~unwind l = List.map (from_stack ~unwind) l
+  let from_env v = v
+  let from_ens v = v
+  let stack_to_env ~reduce ~unwind (k,e,ens,t) =
+   match t with
+      Cic.Const _ as t -> unwind k e ens t    
+    | t -> reduce (k,e,ens,t,[])
+  let compute_to_env ~reduce ~unwind k e ens t =
+   unwind k e ens t
+  let compute_to_stack ~reduce ~unwind k e ens t = (k,e,ens,t)
+ end
+;;
+
+module Reduction(RS : Strategy) =
+ struct
+  type env = RS.env_term list
+  type ens = RS.ens_term Cic.explicit_named_substitution
+  type stack = RS.stack_term list
+  type config = int * env * ens * Cic.term * stack
+
+  (* k is the length of the environment e *)
+  (* m is the current depth inside the term *)
+  let unwind' m k e ens t = 
+   let module C = Cic in
+   let module S = CicSubstitution in
+    if k = 0 && ens = [] then
+     t
+    else 
+     let rec unwind_aux m =
+      function
+         C.Rel n as t ->
+          if n <= m then t else
+           let d =
+            try
+             Some (RS.from_env (List.nth e (n-m-1)))
+            with _ -> None
+           in
+            (match d with 
+                Some t' ->
+                 if m = 0 then t' else S.lift m t'
+              | None -> C.Rel (n-k)
+            )
+       | C.Var (uri,exp_named_subst) ->
+(*
+prerr_endline ("%%%%%UWVAR " ^ String.concat " ; " (List.map (function (uri,t) -> UriManager.string_of_uri uri ^ " := " ^ CicPp.ppterm t) ens)) ;
+*)
+         if List.exists (function (uri',_) -> UriManager.eq uri' uri) ens then
+          CicSubstitution.lift m (RS.from_ens (List.assq uri ens))
+         else
+          let params =
+           (match CicEnvironment.get_obj uri with
+               C.Constant _ -> raise ReferenceToConstant
+             | C.Variable (_,_,_,params) -> params
+             | C.CurrentProof _ -> raise ReferenceToCurrentProof
+             | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
+           )
+          in
+           let exp_named_subst' =
+            substaux_in_exp_named_subst params exp_named_subst m 
+           in
+            C.Var (uri,exp_named_subst')
+       | C.Meta (i,l) ->
+          let l' =
+           List.map
+            (function
+                None -> None
+              | Some t -> Some (unwind_aux m t)
+            ) l
+          in
+           C.Meta (i, l')
+       | C.Sort _ as t -> t
+       | C.Implicit as t -> t
+       | C.Cast (te,ty) -> C.Cast (unwind_aux m te, unwind_aux m ty) (*CSC ???*)
+       | C.Prod (n,s,t) -> C.Prod (n, unwind_aux m s, unwind_aux (m + 1) t)
+       | C.Lambda (n,s,t) -> C.Lambda (n, unwind_aux m s, unwind_aux (m + 1) t)
+       | C.LetIn (n,s,t) -> C.LetIn (n, unwind_aux m s, unwind_aux (m + 1) t)
+       | C.Appl l -> C.Appl (List.map (unwind_aux m) l)
+       | C.Const (uri,exp_named_subst) ->
+          let params =
+           (match CicEnvironment.get_obj uri with
+               C.Constant (_,_,_,params) -> params
+             | C.Variable _ -> raise ReferenceToVariable
+             | C.CurrentProof (_,_,_,_,params) -> params
+             | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
+           )
+          in
+           let exp_named_subst' =
+            substaux_in_exp_named_subst params exp_named_subst m 
+           in
+            C.Const (uri,exp_named_subst')
+       | C.MutInd (uri,i,exp_named_subst) ->
+          let params =
+           (match CicEnvironment.get_obj uri with
+               C.Constant _ -> raise ReferenceToConstant
+             | C.Variable _ -> raise ReferenceToVariable
+             | C.CurrentProof _ -> raise ReferenceToCurrentProof
+             | C.InductiveDefinition (_,params,_) -> params
+           )
+          in
+           let exp_named_subst' =
+            substaux_in_exp_named_subst params exp_named_subst m 
+           in
+            C.MutInd (uri,i,exp_named_subst')
+       | C.MutConstruct (uri,i,j,exp_named_subst) ->
+          let params =
+           (match CicEnvironment.get_obj uri with
+               C.Constant _ -> raise ReferenceToConstant
+             | C.Variable _ -> raise ReferenceToVariable
+             | C.CurrentProof _ -> raise ReferenceToCurrentProof
+             | C.InductiveDefinition (_,params,_) -> params
+           )
+          in
+           let exp_named_subst' =
+            substaux_in_exp_named_subst params exp_named_subst m 
+           in
+            C.MutConstruct (uri,i,j,exp_named_subst')
+       | C.MutCase (sp,i,outt,t,pl) ->
+          C.MutCase (sp,i,unwind_aux m outt, unwind_aux m t,
+           List.map (unwind_aux m) pl)
+       | C.Fix (i,fl) ->
+          let len = List.length fl in
+          let substitutedfl =
+           List.map
+            (fun (name,i,ty,bo) ->
+              (name, i, unwind_aux m ty, unwind_aux (m+len) bo))
+             fl
+          in
+           C.Fix (i, substitutedfl)
+       | C.CoFix (i,fl) ->
+          let len = List.length fl in
+          let substitutedfl =
+           List.map
+            (fun (name,ty,bo) -> (name, unwind_aux m ty, unwind_aux (m+len) bo))
+             fl
+          in
+           C.CoFix (i, substitutedfl)
+     and substaux_in_exp_named_subst params exp_named_subst' m  =
+  (*CSC: Idea di Andrea di ordinare compatibilmente con l'ordine dei params
+      let ens' =
+       List.map (function (uri,t) -> uri, unwind_aux m t) exp_named_subst' @
+  (*CSC: qui liftiamo tutti gli ens anche se magari me ne servono la meta'!!! *)
+        List.map (function (uri,t) -> uri, CicSubstitution.lift m t) ens
+      in
+      let rec filter_and_lift =
+       function
+          [] -> []
+        | uri::tl ->
+           let r = filter_and_lift tl in
+            (try
+              (uri,(List.assq uri ens'))::r
+             with
+              Not_found -> r
+            )
+      in
+       filter_and_lift params
+  *)
+  
+  (*CSC: invece di concatenare sarebbe meglio rispettare l'ordine dei params *)
+  (*CSC: e' vero???? una veloce prova non sembra confermare la teoria        *)
+  
+  (*CSC: codice copiato e modificato dalla cicSubstitution.subst_vars *)
+  (*CSC: codice altamente inefficiente *)
+      let rec filter_and_lift already_instantiated =
+       function
+          [] -> []
+        | (uri,t)::tl when
+            List.for_all
+             (function (uri',_)-> not (UriManager.eq uri uri')) exp_named_subst'
+            &&
+             not (List.mem uri already_instantiated)
+            &&
+             List.mem uri params
+           ->
+            (uri,CicSubstitution.lift m (RS.from_ens t)) ::
+             (filter_and_lift (uri::already_instantiated) tl)
+        | _::tl -> filter_and_lift already_instantiated tl
+(*
+        | (uri,_)::tl ->
+prerr_endline ("---- SKIPPO " ^ UriManager.string_of_uri uri) ;
+if List.for_all (function (uri',_) -> not (UriManager.eq uri uri')) exp_named_subst' then prerr_endline "---- OK1" ;
+prerr_endline ("++++ uri " ^ UriManager.string_of_uri uri ^ " not in " ^ String.concat " ; " (List.map UriManager.string_of_uri params)) ;
+if List.mem uri params then prerr_endline "---- OK2" ;
+        filter_and_lift tl
+*)
+      in
+       List.map (function (uri,t) -> uri, unwind_aux m t) exp_named_subst' @
+        (filter_and_lift [] (List.rev ens))
+     in
+      unwind_aux m t          
+  ;;
+  
+  let unwind =
+   unwind' 0 
+  ;;
+  
+  let reduce context : config -> Cic.term = 
+   let module C = Cic in
+   let module S = CicSubstitution in 
+   let rec reduce =
     function
-      (k, e, (C.Rel n as t), s) -> let d =
-(* prerr_string ("Rel " ^ string_of_int n) ; flush stderr ; *)
-                                   try Some (List.nth e (n-1))
-                                   with _ -> None
-                                 in (match d with 
-                                   Some t' -> reduce (0, [],t',s)
-                                 | None -> if s = [] then C.Rel (n-k)
-                                           else C.Appl (C.Rel (n-k)::s))
-    | (k, e, (C.Var uri as t), s) -> 
-        (match CicEnvironment.get_cooked_obj uri 0 with
-            C.Definition _ -> raise ReferenceToDefinition
-          | C.Axiom _ -> raise ReferenceToAxiom
-          | C.CurrentProof _ -> raise ReferenceToCurrentProof
-          | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
-          | C.Variable (_,None,_) -> if s = [] then t else C.Appl (t::s)
-          | C.Variable (_,Some body,_) -> reduce (0, [], body, s)
+       (k, e, _, (C.Rel n as t), s) ->
+        let d =
+         try
+          Some (RS.from_env (List.nth e (n-1)))
+         with
+          _ ->
+           try
+            begin
+             match List.nth context (n - 1 - k) with
+                None -> assert false
+              | Some (_,C.Decl _) -> None
+              | Some (_,C.Def x) -> Some (S.lift (n - k) x)
+            end
+           with
+            _ -> None
+        in
+         (match d with 
+             Some t' -> reduce (0,[],[],t',s)
+           | None ->
+              if s = [] then
+               C.Rel (n-k)
+              else C.Appl (C.Rel (n-k)::(RS.from_stack_list ~unwind s))
          )
-    | (k, e, (C.Meta _ as t), s) -> if s = [] then t 
-                                 else C.Appl (t::s)
-    | (k, e, (C.Sort _ as t), s) -> t (* s should be empty *)
-    | (k, e, (C.Implicit as t), s) -> t (* s should be empty *)
-    | (k, e, (C.Cast (te,ty) as t), s) -> reduce (k, e,te,s) (* s should be empty *)
-    | (k, e, (C.Prod _ as t), s) -> unwind k e t (* s should be empty *)
-    | (k, e, (C.Lambda (_,_,t) as t'), []) -> unwind k e t' 
-    | (k, e, C.Lambda (_,_,t), p::s) ->
-(* prerr_string ("Lambda body: " ^ CicPp.ppterm t) ; flush stderr ; *)
-        reduce (k+1, p::e,t,s) 
-    | (k, e, (C.LetIn (_,m,t) as t'), s) -> let m' = reduce (k,e,m,[]) in
-                                         reduce (k+1, m'::e,t,s)
-    | (k, e, C.Appl [], s) -> raise (Impossible 1)
-    (* this is lazy 
-    | (k, e, C.Appl (he::tl), s) ->  let tl' = List.map (unwind k e) tl
-                                 in reduce (k, e, he, (List.append tl' s)) *)
-    (* this is strict *)
-    | (k, e, C.Appl (he::tl), s) ->
-                                  (* constants are NOT unfolded *)
-                                  let red = function
-                                      C.Const _ as t -> t    
-                                    | t -> reduce (k, e,t,[]) in
-                                  let tl' = List.map red tl in
-                                   reduce (k, e, he , List.append tl' s) 
-(* 
-    | (k, e, C.Appl ((C.Lambda _ as he)::tl), s) 
-    | (k, e, C.Appl ((C.Const _ as he)::tl), s)  
-    | (k, e, C.Appl ((C.MutCase _ as he)::tl), s) 
-    | (k, e, C.Appl ((C.Fix _ as he)::tl), s) ->
-(* strict evaluation, but constants are NOT
-                                    unfolded *)
-                                  let red = function
-                                      C.Const _ as t -> t    
-                                    | t -> reduce (k, e,t,[]) in
-                                  let tl' = List.map red tl in
-                                   reduce (k, e, he , List.append tl' s)
-    | (k, e, C.Appl l, s) -> C.Appl (List.append (List.map (unwind k e) l) s) *)
-    | (k, e, (C.Const (uri,cookingsno) as t), s) ->
-       (match CicEnvironment.get_cooked_obj uri cookingsno with
-           C.Definition (_,body,_,_) -> reduce (0, [], body, s) 
-                                        (* constants are closed *)
-         | C.Axiom _ -> if s = [] then t else C.Appl (t::s)
-         | C.Variable _ -> raise ReferenceToVariable
-         | C.CurrentProof (_,_,body,_) -> reduce (0, [], body, s)
-         | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
-       )
-    | (k, e, (C.MutInd (uri,_,_) as t),s) -> let t' = unwind k e t in 
-                                         if s = [] then t' else C.Appl (t'::s)
-    | (k, e, (C.MutConstruct (uri,_,_,_) as t),s) -> 
-                                          let t' = unwind k e t in
-                                          if s = [] then t' else C.Appl (t'::s)
-    | (k, e, (C.MutCase (mutind,cookingsno,i,_,term,pl) as t),s) ->
+     | (k, e, ens, (C.Var (uri,exp_named_subst) as t), s) -> 
+         if List.exists (function (uri',_) -> UriManager.eq uri' uri) ens then
+          reduce (0, [], [], RS.from_ens (List.assq uri ens), s)
+         else
+          (match CicEnvironment.get_obj uri with
+              C.Constant _ -> raise ReferenceToConstant
+            | C.CurrentProof _ -> raise ReferenceToCurrentProof
+            | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
+            | C.Variable (_,None,_,_) ->
+               let t' = unwind k e ens t in
+                if s = [] then t' else
+                 C.Appl (t'::(RS.from_stack_list ~unwind s))
+            | C.Variable (_,Some body,_,_) ->
+               let ens' = push_exp_named_subst k e ens exp_named_subst in
+                reduce (0, [], ens', body, s)
+          )
+     | (k, e, ens, (C.Meta _ as t), s) ->
+        let t' = unwind k e ens t in
+         if s = [] then t' else C.Appl (t'::(RS.from_stack_list ~unwind s))
+     | (k, e, _, (C.Sort _ as t), s) -> t (* s should be empty *)
+     | (k, e, _, (C.Implicit as t), s) -> t (* s should be empty *)
+     | (k, e, ens, (C.Cast (te,ty) as t), s) ->
+        reduce (k, e, ens, te, s) (* s should be empty *)
+     | (k, e, ens, (C.Prod _ as t), s) ->
+         unwind k e ens t (* s should be empty *)
+     | (k, e, ens, (C.Lambda (_,_,t) as t'), []) -> unwind k e ens t' 
+     | (k, e, ens, C.Lambda (_,_,t), p::s) ->
+         reduce (k+1, (RS.stack_to_env ~reduce ~unwind p)::e, ens, t,s)
+     | (k, e, ens, (C.LetIn (_,m,t) as t'), s) ->
+        let m' = RS.compute_to_env ~reduce ~unwind k e ens m in
+         reduce (k+1, m'::e, ens, t, s)
+     | (_, _, _, C.Appl [], _) -> raise (Impossible 1)
+     | (k, e, ens, C.Appl (he::tl), s) ->
+        let tl' =
+         List.map
+          (function t -> RS.compute_to_stack ~reduce ~unwind k e ens t) tl
+        in
+         reduce (k, e, ens, he, (List.append tl') s)
+  (* CSC: Old Dead Code 
+     | (k, e, ens, C.Appl ((C.Lambda _ as he)::tl), s) 
+     | (k, e, ens, C.Appl ((C.Const _ as he)::tl), s)  
+     | (k, e, ens, C.Appl ((C.MutCase _ as he)::tl), s) 
+     | (k, e, ens, C.Appl ((C.Fix _ as he)::tl), s) ->
+  (* strict evaluation, but constants are NOT unfolded *)
+        let red =
+         function
+            C.Const _ as t -> unwind k e ens t
+          | t -> reduce (k,e,ens,t,[])
+        in
+         let tl' = List.map red tl in
+          reduce (k, e, ens, he , List.append tl' s)
+     | (k, e, ens, C.Appl l, s) ->
+         C.Appl (List.append (List.map (unwind k e ens) l) s)
+  *)
+     | (k, e, ens, (C.Const (uri,exp_named_subst) as t), s) ->
+        (match CicEnvironment.get_obj uri with
+            C.Constant (_,Some body,_,_) ->
+             let ens' = push_exp_named_subst k e ens exp_named_subst in
+              (* constants are closed *)
+              reduce (0, [], ens', body, s) 
+          | C.Constant (_,None,_,_) ->
+             let t' = unwind k e ens t in
+              if s = [] then t' else C.Appl (t'::(RS.from_stack_list ~unwind s))
+          | C.Variable _ -> raise ReferenceToVariable
+          | C.CurrentProof (_,_,body,_,_) ->
+             let ens' = push_exp_named_subst k e ens exp_named_subst in
+              (* constants are closed *)
+              reduce (0, [], ens', body, s)
+          | C.InductiveDefinition _ -> raise ReferenceToInductiveDefinition
+        )
+     | (k, e, ens, (C.MutInd _ as t),s) ->
+        let t' = unwind k e ens t in 
+         if s = [] then t' else C.Appl (t'::(RS.from_stack_list ~unwind s))
+     | (k, e, ens, (C.MutConstruct _ as t),s) -> 
+         let t' = unwind k e ens t in
+          if s = [] then t' else C.Appl (t'::(RS.from_stack_list ~unwind s))
+     | (k, e, ens, (C.MutCase (mutind,i,_,term,pl) as t),s) ->
         let decofix =
-        function
-           C.CoFix (i,fl) as t ->
-            let (_,_,body) = List.nth fl i in
-             let body' =
-              let counter = ref (List.length fl) in
-               List.fold_right
-                (fun _ -> decr counter ; S.subst (C.CoFix (!counter,fl)))
-                fl
-                body
-             in
-              reduce (0,[],body',[])
-         | C.Appl (C.CoFix (i,fl) :: tl) ->
-            let (_,_,body) = List.nth fl i in
-             let body' =
-              let counter = ref (List.length fl) in
-               List.fold_right
-                (fun _ -> decr counter ; S.subst (C.CoFix (!counter,fl)))
-                fl
-                body
-             in
-              reduce (0,[], body', tl)
-         | t -> t
-       in
-        (match decofix (reduce (k, e,term,[])) with
-            C.MutConstruct (_,_,_,j) -> reduce (k, e, (List.nth pl (j-1)), s)
-          | C.Appl (C.MutConstruct (_,_,_,j) :: tl) ->
-             let (arity, r, num_ingredients) =
-              match CicEnvironment.get_obj mutind with
-                 C.InductiveDefinition (tl,ingredients,r) ->
-                   let (_,_,arity,_) = List.nth tl i
-                   and num_ingredients =
-                    List.fold_right
-                     (fun (k,l) i ->
-                       if k < cookingsno then i + List.length l else i
-                     ) ingredients 0
-                   in
-                    (arity,r,num_ingredients)
-               | _ -> raise WrongUriToInductiveDefinition
-             in
-              let ts =
-               let num_to_eat = r + num_ingredients in
-                let rec eat_first =
-                 function
-                    (0,l) -> l
-                  | (n,he::tl) when n > 0 -> eat_first (n - 1, tl)
-                  | _ -> raise (Impossible 5)
-                in
-                 eat_first (num_to_eat,tl)
+         function
+            C.CoFix (i,fl) as t ->
+             let (_,_,body) = List.nth fl i in
+              let body' =
+               let counter = ref (List.length fl) in
+                List.fold_right
+                 (fun _ -> decr counter ; S.subst (C.CoFix (!counter,fl)))
+                 fl
+                 body
               in
-               reduce (k, e, (List.nth pl (j-1)),(ts@s)) 
-         | C.Cast _ | C.Implicit ->
-            raise (Impossible 2) (* we don't trust our whd ;-) *)
-         | _ -> let t' = unwind k e t in
-                if s = [] then t' else C.Appl (t'::s)
-       )
-    | (k, e, (C.Fix (i,fl) as t), s) ->
-       let (_,recindex,_,body) = List.nth fl i in
-        let recparam =
-         try
-          Some (List.nth s recindex)
-         with
-          _ -> None
+               (* the term is the result of a reduction; *)
+               (* so it is already unwinded.             *)
+               reduce (0,[],[],body',[])
+          | C.Appl (C.CoFix (i,fl) :: tl) ->
+             let (_,_,body) = List.nth fl i in
+              let body' =
+               let counter = ref (List.length fl) in
+                List.fold_right
+                 (fun _ -> decr counter ; S.subst (C.CoFix (!counter,fl)))
+                 fl
+                 body
+              in
+               (* the term is the result of a reduction; *)
+               (* so it is already unwinded.             *)
+               reduce (0,[],[],body',RS.to_stack_list tl)
+          | t -> t
         in
-         (match recparam with
-             Some recparam ->
-              (match reduce (0,[],recparam,[]) with
-                  (* match recparam with *) 
-                  C.MutConstruct _
-                | C.Appl ((C.MutConstruct _)::_) ->
-                   (* OLD 
-                   let body' =
-                    let counter = ref (List.length fl) in
-                     List.fold_right
-                      (fun _ -> decr counter ; S.subst (C.Fix (!counter,fl)))
-                      fl
-                      body
-                   in 
-                    reduce (k, e, body', s) *)
-                   (* NEW *)
-                   let leng = List.length fl in
-                   let fl' = 
-                       let unwind_fl (name,recindex,typ,body) = 
-                           (name,recindex,unwind' leng k e typ, unwind' leng k e body)  in
-                      List.map unwind_fl fl in 
-                   let new_env =
-                     let counter = ref leng in
-                     let rec build_env e =
-                         if !counter = 0 then e else (decr counter; 
-                                    build_env ((C.Fix (!counter,fl'))::e)) in
-                     build_env e in
-                   reduce (k+leng, new_env, body,s)  
-               | _ -> let t' = unwind k e t in 
-                      if s = [] then t' else C.Appl (t'::s)
-             )
-          | None -> let t' = unwind k e t in 
-                    if s = [] then t' else C.Appl (t'::s)
+         (match decofix (reduce (k,e,ens,term,[])) with
+             C.MutConstruct (_,_,j,_) ->
+              reduce (k, e, ens, (List.nth pl (j-1)), s)
+           | C.Appl (C.MutConstruct (_,_,j,_) :: tl) ->
+              let (arity, r) =
+               match CicEnvironment.get_obj mutind with
+                  C.InductiveDefinition (tl,ingredients,r) ->
+                    let (_,_,arity,_) = List.nth tl i in
+                     (arity,r)
+                | _ -> raise WrongUriToInductiveDefinition
+              in
+               let ts =
+                let num_to_eat = r in
+                 let rec eat_first =
+                  function
+                     (0,l) -> l
+                   | (n,he::tl) when n > 0 -> eat_first (n - 1, tl)
+                   | _ -> raise (Impossible 5)
+                 in
+                  eat_first (num_to_eat,tl)
+               in
+                (* ts are already unwinded because they are a sublist of tl *)
+                reduce (k, e, ens, (List.nth pl (j-1)), (RS.to_stack_list ts)@s)
+          | C.Cast _ | C.Implicit ->
+              raise (Impossible 2) (* we don't trust our whd ;-) *)
+           | _ ->
+             let t' = unwind k e ens t in
+              if s = [] then t' else C.Appl (t'::(RS.from_stack_list ~unwind s))
          )
-    | (k, e,(C.CoFix (i,fl) as t),s) -> let t' = unwind k e t in 
-       if s = [] then t' else C.Appl (t'::s);;
-
-let rec whd = let module C = Cic in
+     | (k, e, ens, (C.Fix (i,fl) as t), s) ->
+        let (_,recindex,_,body) = List.nth fl i in
+         let recparam =
+          try
+           Some (RS.from_stack ~unwind (List.nth s recindex))
+          with
+           _ -> None
+         in
+          (match recparam with
+              Some recparam ->
+               (match reduce (0,[],[],recparam,[]) with
+                   (* match recparam with *) 
+                   C.MutConstruct _
+                 | C.Appl ((C.MutConstruct _)::_) ->
+                    (* OLD 
+                    let body' =
+                     let counter = ref (List.length fl) in
+                      List.fold_right
+                       (fun _ -> decr counter ; S.subst (C.Fix (!counter,fl)))
+                       fl
+                       body
+                    in 
+                     reduce (k, e, ens, body', s) *)
+                    (* NEW *)
+                    let leng = List.length fl in
+                    let fl' = 
+                     let unwind_fl (name,recindex,typ,body) = 
+                      (name,recindex,unwind k e ens typ,
+                        unwind' leng k e ens body)
+                     in
+                     List.map unwind_fl fl
+                    in
+                     let new_env =
+                      let counter = ref 0 in
+                      let rec build_env e =
+                       if !counter = leng then e
+                       else
+                        (incr counter ;
+                         build_env ((RS.to_env (C.Fix (!counter -1, fl')))::e))
+                      in
+                       build_env e
+                     in
+                      reduce (k+leng, new_env, ens, body, s)  
+                 | _ ->
+                   let t' = unwind k e ens t in 
+                    if s = [] then t' else
+                     C.Appl (t'::(RS.from_stack_list ~unwind s))
+               )
+            | None ->
+               let t' = unwind k e ens t in 
+                if s = [] then t' else
+                 C.Appl (t'::(RS.from_stack_list ~unwind s))
+          )
+     | (k, e, ens, (C.CoFix (i,fl) as t),s) ->
+        let t' = unwind k e ens t in 
+         if s = [] then t' else C.Appl (t'::(RS.from_stack_list ~unwind s))
+   and push_exp_named_subst k e ens =
     function
-      C.Rel _ as t -> t
-    | C.Var _ as t -> reduce (0, [], t, [])
-    | C.Meta _ as t -> t
-    | C.Sort _ as t -> t
-    | C.Implicit as t -> t
-    | C.Cast (te,ty) -> whd te
-    | C.Prod _ as t -> t
-    | C.Lambda _ as t -> t
-    | C.LetIn (n,s,t) -> reduce (1, [s], t, [])
-    | C.Appl [] -> raise (Impossible 1)
-    | C.Appl (he::tl) -> reduce (0, [], he, tl)
-    | C.Const _ as t -> reduce (0, [], t, [])
-    | C.MutInd _ as t -> t
-    | C.MutConstruct _ as t -> t
-    | C.MutCase _ as t -> reduce (0, [], t, [])
-    | C.Fix _ as t -> reduce (0, [], t, [])
-    | C.CoFix _ as t -> reduce (0, [], t, [])
-    ;;
-
-(* let whd t = reduce (0, [],t,[]);; 
- let res = reduce (0, [],t,[]) in
- let rescsc = CicReductionNaif.whd t in
-  if not (CicReductionNaif.are_convertible res rescsc) then
+       [] -> ens
+     | (uri,t)::tl ->
+         push_exp_named_subst k e ((uri,RS.to_ens (unwind k e ens t))::ens) tl
+   in
+    reduce
+  ;;
+  
+  let rec whd context t = reduce context (0, [], [], t, []);;
+  
+(* DEBUGGING ONLY
+let whd context t =
+ let res = whd context t in
+ let rescsc = CicReductionNaif.whd context t in
+  if not (CicReductionNaif.are_convertible context res rescsc) then
    begin
     prerr_endline ("PRIMA: " ^ CicPp.ppterm t) ;
     flush stderr ;
@@ -310,74 +740,155 @@ let rec whd = let module C = Cic in
     flush stderr ;
     prerr_endline ("CSC: " ^ CicPp.ppterm rescsc) ;
     flush stderr ;
+CicReductionNaif.fdebug := 0 ;
+let _ =  CicReductionNaif.are_convertible context res rescsc in
     assert false ;
    end
   else 
-   res ;; *)
+   res
+;;
+*)
+ end
+;;
+
 
+(*
+module R = Reduction CallByNameStrategy;;
+module R = Reduction CallByValueStrategy;;
+module R = Reduction CallByValueStrategyByNameOnConstants;;
+module R = Reduction LazyCallByValueStrategy;;
+module R = Reduction LazyCallByValueStrategyByNameOnConstants;;
+module R = Reduction LazyCallByNameStrategy;;
+module R = Reduction
+ LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns;;
+module R = Reduction ClosuresOnStackByValueFromEnvOrEnsStrategy;;
+module R = Reduction
+ ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy;;
+*)
+module R = Reduction ClosuresOnStackByValueFromEnvOrEnsStrategy;;
+
+let whd = R.whd;;
 
 (* t1, t2 must be well-typed *)
-let are_convertible = 
- let rec aux t1 t2 = 
- if t1 = t2 then true
- else
+let are_convertible =
+ let module U = UriManager in
+ let rec aux context t1 t2 =
   let aux2 t1 t2 =
-   let module U = UriManager in
-   let module C = Cic in 
-      match (t1,t2) with
-         (C.Rel n1, C.Rel n2) -> n1 = n2
-       | (C.Var uri1, C.Var uri2) -> U.eq uri1 uri2
-       | (C.Meta n1, C.Meta n2) -> n1 = n2
-       | (C.Sort s1, C.Sort s2) -> true (*CSC da finire con gli universi *)
-       | (C.Prod (_,s1,t1), C.Prod(_,s2,t2)) ->
-          aux s1 s2 && aux t1 t2
-       | (C.Lambda (_,s1,t1), C.Lambda(_,s2,t2)) ->
-          aux s1 s2 && aux t1 t2
-       | (C.Appl l1, C.Appl l2) ->
-          (try
-            List.fold_right2 (fun  x y b -> aux x y && b) l1 l2 true 
-           with
-            Invalid_argument _ -> false
-          )
-       | (C.Const (uri1,_), C.Const (uri2,_)) ->
-           U.eq uri1 uri2 
-       | (C.MutInd (uri1,k1,i1), C.MutInd (uri2,k2,i2)) ->
-           U.eq uri1 uri2 && i1 = i2
-       | (C.MutConstruct (uri1,_,i1,j1), C.MutConstruct (uri2,_,i2,j2)) ->
-           U.eq uri1 uri2 && i1 = i2 && j1 = j2
-       | (C.MutCase (uri1,_,i1,outtype1,term1,pl1),
-          C.MutCase (uri2,_,i2,outtype2,term2,pl2)) -> 
-           (* aux outtype1 outtype2 should be true if aux pl1 pl2 *)
-           U.eq uri1 uri2 && i1 = i2 && aux outtype1 outtype2 &&
-            aux term1 term2 &&
-            List.fold_right2 (fun x y b -> b && aux x y) pl1 pl2 true
-       | (C.Fix (i1,fl1), C.Fix (i2,fl2)) ->
-          i1 = i2 &&
-           List.fold_right2
-            (fun (_,recindex1,ty1,bo1) (_,recindex2,ty2,bo2) b ->
-              b && recindex1 = recindex2 && aux ty1 ty2 && aux bo1 bo2)
-            fl1 fl2 true
-       | (C.CoFix (i1,fl1), C.CoFix (i2,fl2)) ->
-          i1 = i2 &&
-           List.fold_right2
-            (fun (_,ty1,bo1) (_,ty2,bo2) b ->
-              b && aux ty1 ty2 && aux bo1 bo2)
-            fl1 fl2 true
-       | (_,_) -> false
+   (* this trivial euristic cuts down the total time of about five times ;-) *)
+   (* this because most of the time t1 and t2 are "sintactically" the same   *)
+   if t1 = t2 then
+    true
+   else
+    begin
+     let module C = Cic in
+       match (t1,t2) with
+          (C.Rel n1, C.Rel n2) -> n1 = n2
+        | (C.Var (uri1,exp_named_subst1), C.Var (uri2,exp_named_subst2)) ->
+            U.eq uri1 uri2 &&
+             (try
+               List.fold_right2
+                (fun (uri1,x) (uri2,y) b ->
+                  U.eq uri1 uri2 && aux context x y && b
+                ) exp_named_subst1 exp_named_subst2 true 
+              with
+               Invalid_argument _ -> false
+             )
+        | (C.Meta (n1,l1), C.Meta (n2,l2)) -> 
+            n1 = n2 &&
+             List.fold_left2
+              (fun b t1 t2 ->
+                b &&
+                 match t1,t2 with
+                    None,_
+                  | _,None  -> true
+                  | Some t1',Some t2' -> aux context t1' t2'
+              ) true l1 l2
+        | (C.Sort s1, C.Sort s2) -> true (*CSC da finire con gli universi *)
+        | (C.Prod (name1,s1,t1), C.Prod(_,s2,t2)) ->
+           aux context s1 s2 && aux ((Some (name1, (C.Decl s1)))::context) t1 t2
+        | (C.Lambda (name1,s1,t1), C.Lambda(_,s2,t2)) ->
+           aux context s1 s2 && aux ((Some (name1, (C.Decl s1)))::context) t1 t2
+        | (C.LetIn (name1,s1,t1), C.LetIn(_,s2,t2)) ->
+           aux context s1 s2 && aux ((Some (name1, (C.Def s1)))::context) t1 t2
+        | (C.Appl l1, C.Appl l2) ->
+           (try
+             List.fold_right2 (fun  x y b -> aux context x y && b) l1 l2 true 
+            with
+             Invalid_argument _ -> false
+           )
+        | (C.Const (uri1,exp_named_subst1), C.Const (uri2,exp_named_subst2)) ->
+            U.eq uri1 uri2 &&
+             (try
+               List.fold_right2
+                (fun (uri1,x) (uri2,y) b ->
+                  U.eq uri1 uri2 && aux context x y && b
+                ) exp_named_subst1 exp_named_subst2 true 
+              with
+               Invalid_argument _ -> false
+             )
+        | (C.MutInd (uri1,i1,exp_named_subst1),
+           C.MutInd (uri2,i2,exp_named_subst2)
+          ) ->
+            U.eq uri1 uri2 && i1 = i2 &&
+             (try
+               List.fold_right2
+                (fun (uri1,x) (uri2,y) b ->
+                  U.eq uri1 uri2 && aux context x y && b
+                ) exp_named_subst1 exp_named_subst2 true 
+              with
+               Invalid_argument _ -> false
+             )
+        | (C.MutConstruct (uri1,i1,j1,exp_named_subst1),
+           C.MutConstruct (uri2,i2,j2,exp_named_subst2)
+          ) ->
+            U.eq uri1 uri2 && i1 = i2 && j1 = j2 &&
+             (try
+               List.fold_right2
+                (fun (uri1,x) (uri2,y) b ->
+                  U.eq uri1 uri2 && aux context x y && b
+                ) exp_named_subst1 exp_named_subst2 true 
+              with
+               Invalid_argument _ -> false
+             )
+        | (C.MutCase (uri1,i1,outtype1,term1,pl1),
+           C.MutCase (uri2,i2,outtype2,term2,pl2)) -> 
+            U.eq uri1 uri2 && i1 = i2 && aux context outtype1 outtype2 &&
+             aux context term1 term2 &&
+             List.fold_right2 (fun x y b -> b && aux context x y) pl1 pl2 true
+        | (C.Fix (i1,fl1), C.Fix (i2,fl2)) ->
+           let tys =
+            List.map (function (n,_,ty,_) -> Some (C.Name n,(C.Decl ty))) fl1
+           in
+            i1 = i2 &&
+             List.fold_right2
+              (fun (_,recindex1,ty1,bo1) (_,recindex2,ty2,bo2) b ->
+                b && recindex1 = recindex2 && aux context ty1 ty2 &&
+                 aux (tys@context) bo1 bo2)
+              fl1 fl2 true
+        | (C.CoFix (i1,fl1), C.CoFix (i2,fl2)) ->
+           let tys =
+            List.map (function (n,ty,_) -> Some (C.Name n,(C.Decl ty))) fl1
+           in
+            i1 = i2 &&
+             List.fold_right2
+              (fun (_,ty1,bo1) (_,ty2,bo2) b ->
+                b && aux context ty1 ty2 && aux (tys@context) bo1 bo2)
+              fl1 fl2 true
+        | (C.Cast _, _) | (_, C.Cast _)
+        | (C.Implicit, _) | (_, C.Implicit) ->
+           raise (Impossible 3) (* we don't trust our whd ;-) *)
+        | (_,_) -> false
+    end
   in
    if aux2 t1 t2 then true
-   else aux2 (whd t1) (whd t2)
-in
- aux 
-;; 
-
-
-
-
-
-
-
-
-
-
-
+   else
+    begin
+     debug t1 [t2] "PREWHD";
+     let t1' = whd context t1 in
+     let t2' = whd context t2 in
+      debug t1' [t2'] "POSTWHD";
+      aux2 t1' t2'
+    end
+ in
+  aux
+;;