(* Copyright (C) 2005, HELM Team. * * This file is part of HELM, an Hypertextual, Electronic * Library of Mathematics, developed at the Computer Science * Department, University of Bologna, Italy. * * HELM is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * HELM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HELM; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * * For details, see the HELM World-Wide-Web page, * http://cs.unibo.it/helm/. *) (* let _profiler = <:profiler<_profiler>>;; *) (* $Id$ *) open Utils;; open Printf;; type auto_type = AutoTypes.flags -> ProofEngineTypes.proof -> Cic.context -> ?universe:AutoTypes.universe -> AutoTypes.cache -> ProofEngineTypes.goal list -> Cic.substitution list * AutoTypes.cache * AutoTypes.universe let debug_print s = prerr_endline (Lazy.force s);; let check_disjoint_invariant subst metasenv msg = if (List.exists (fun (i,_,_) -> (Subst.is_in_subst i subst)) metasenv) then begin prerr_endline ("not disjoint: " ^ msg); assert false end ;; let rec check_irl start = function | [] -> true | None::tl -> check_irl (start+1) tl | (Some (Cic.Rel x))::tl -> if x = start then check_irl (start+1) tl else false | _ -> false ;; let rec is_simple_term = function | Cic.Appl ((Cic.Meta _)::_) -> false | Cic.Appl l -> List.for_all is_simple_term l | Cic.Meta (i, l) -> let l = [] in check_irl 1 l | Cic.Rel _ -> true | Cic.Const _ -> true | Cic.MutInd (_, _, []) -> true | Cic.MutConstruct (_, _, _, []) -> true | _ -> false ;; let locked menv i = List.exists (fun (j,_,_) -> i = j) menv ;; let unification_simple locked_menv metasenv context t1 t2 ugraph = let module C = Cic in let module M = CicMetaSubst in let module U = CicUnification in let lookup = Subst.lookup_subst in let rec occurs_check subst what where = match where with | Cic.Meta(i,_) when i = what -> true | C.Appl l -> List.exists (occurs_check subst what) l | C.Meta _ -> let t = lookup where subst in if t <> where then occurs_check subst what t else false | _ -> false in let rec unif subst menv s t = let s = match s with C.Meta _ -> lookup s subst | _ -> s and t = match t with C.Meta _ -> lookup t subst | _ -> t in match s, t with | s, t when s = t -> subst, menv (* sometimes the same meta has different local contexts; this could create "cyclic" substitutions *) | C.Meta (i, _), C.Meta (j, _) when i=j -> subst, menv | C.Meta (i, _), C.Meta (j, _) when (locked locked_menv i) &&(locked locked_menv j) -> raise (U.UnificationFailure (lazy "Inference.unification.unif")) | C.Meta (i, _), C.Meta (j, _) when (locked locked_menv i) -> unif subst menv t s | C.Meta (i, _), C.Meta (j, _) when (i > j) && not (locked locked_menv j) -> unif subst menv t s | C.Meta (i,_), t when occurs_check subst i t -> raise (U.UnificationFailure (lazy "Inference.unification.unif")) | C.Meta (i, l), t when (locked locked_menv i) -> raise (U.UnificationFailure (lazy "Inference.unification.unif")) | C.Meta (i, l), t -> ( try let _, _, ty = CicUtil.lookup_meta i menv in assert (not (Subst.is_in_subst i subst)); let subst = Subst.buildsubst i context t ty subst in let menv = menv in (* List.filter (fun (m, _, _) -> i <> m) menv in *) subst, menv with CicUtil.Meta_not_found m -> let names = names_of_context context in (*debug_print (lazy*) prerr_endline (Printf.sprintf "Meta_not_found %d!: %s %s\n%s\n\n%s" m (CicPp.pp t1 names) (CicPp.pp t2 names) (print_metasenv menv) (print_metasenv metasenv)); assert false ) | _, C.Meta _ -> unif subst menv t s | C.Appl (hds::_), C.Appl (hdt::_) when hds <> hdt -> raise (U.UnificationFailure (lazy "Inference.unification.unif")) | C.Appl (hds::tls), C.Appl (hdt::tlt) -> ( try List.fold_left2 (fun (subst', menv) s t -> unif subst' menv s t) (subst, menv) tls tlt with Invalid_argument _ -> raise (U.UnificationFailure (lazy "Inference.unification.unif")) ) | _, _ -> raise (U.UnificationFailure (lazy "Inference.unification.unif")) in let subst, menv = unif Subst.empty_subst metasenv t1 t2 in let menv = Subst.filter subst menv in subst, menv, ugraph ;; let profiler = HExtlib.profile "P/Inference.unif_simple[flatten]" let profiler2 = HExtlib.profile "P/Inference.unif_simple[flatten_fast]" let profiler3 = HExtlib.profile "P/Inference.unif_simple[resolve_meta]" let profiler4 = HExtlib.profile "P/Inference.unif_simple[filter]" let unification_aux b metasenv1 metasenv2 context t1 t2 ugraph = let metasenv = HExtlib.list_uniq (List.sort Pervasives.compare (metasenv1 @ metasenv2)) (* metasenv1 @ metasenv2 *) in let subst, menv, ug = if not (is_simple_term t1) || not (is_simple_term t2) then ( debug_print (lazy (Printf.sprintf "NOT SIMPLE TERMS: %s %s" (CicPp.ppterm t1) (CicPp.ppterm t2))); raise (CicUnification.UnificationFailure (lazy "Inference.unification.unif")) ) else if b then (* full unification *) unification_simple [] metasenv context t1 t2 ugraph else (* matching: metasenv1 is locked *) unification_simple metasenv1 metasenv context t1 t2 ugraph in if Utils.debug_res then ignore(check_disjoint_invariant subst menv "unif"); (* let flatten subst = List.map (fun (i, (context, term, ty)) -> let context = apply_subst_context subst context in let term = apply_subst subst term in let ty = apply_subst subst ty in (i, (context, term, ty))) subst in let flatten subst = profiler.HExtlib.profile flatten subst in let subst = flatten subst in *) subst, menv, ug ;; exception MatchingFailure;; (** matching takes in input the _disjoint_ metasenv of t1 and t2; it perform unification in the union metasenv, then check that the first metasenv has not changed *) let matching metasenv1 metasenv2 context t1 t2 ugraph = try unification_aux false metasenv1 metasenv2 context t1 t2 ugraph with CicUnification.UnificationFailure _ -> raise MatchingFailure ;; let unification m1 m2 c t1 t2 ug = try unification_aux true m1 m2 c t1 t2 ug with exn -> raise exn ;; let check_eq context msg eq = let w, proof, (eq_ty, left, right, order), metas = eq in if not (fst (CicReduction.are_convertible ~metasenv:metas context eq_ty (fst (CicTypeChecker.type_of_aux' metas context left CicUniv.empty_ugraph)) CicUniv.empty_ugraph)) then begin prerr_endline msg; assert false; end else () ;; let default_auto _ _ _ ?(universe:AutoTypes.universe option) _ _ = [],AutoTypes.cache_empty,AutoTypes.empty_universe ;; (* far sta roba e' un casino perche' bisogna pulire il contesto lasciando solo * la roba che non dipende da i *) let pi_of_ctx t i ctx = let rec aux j = function | [] -> t | (Some (nam, Cic.Decl (term)))::tl -> Cic.Prod(nam,term,aux (j-1) tl) | _ -> assert false (* not implemented *) in aux (List.length ctx-1) (List.rev ctx) ;; let lambda_of_ctx t i ctx = let rec aux j = function | [] -> t | (Some (nam, Cic.Decl (term)))::tl -> Cic.Lambda(nam,term,aux (j-1) tl) | _ -> assert false (* not implemented *) in aux (List.length ctx -1) (List.rev ctx) ;; let rec skip_lambda t l = match t,l with | Cic.Lambda (_,_,t), _::((_::_) as tl) -> skip_lambda t tl | Cic.Lambda (_,_,t), _::[] -> t | _ -> assert false ;; let ty_of_eq = function | Cic.Appl [Cic.MutInd (_, _, _); ty; _; _] -> ty | _ -> assert false ;; exception UnableToSaturate of AutoTypes.universe option * AutoTypes.cache let saturate_term context metasenv oldnewmeta term ?universe cache auto fast = let head, metasenv, args, newmeta = ProofEngineHelpers.saturate_term oldnewmeta metasenv context term 0 in let args_for_auto = HExtlib.filter_map (function | Cic.Meta(i,_) -> let _,_,mt = CicUtil.lookup_meta i metasenv in let sort,u = CicTypeChecker.type_of_aux' metasenv context mt CicUniv.empty_ugraph in let b, _ = CicReduction.are_convertible ~metasenv context sort (Cic.Sort Cic.Prop) u in if b then Some i else None (* maybe unif or conv should be used instead of = *) | _ -> assert false) args in let results,universe,cache = if args_for_auto = [] then let newmetas = List.filter (fun (i,_,_) -> i >= oldnewmeta) metasenv in [args,metasenv,newmetas,head,newmeta],universe,cache else let proof = None,metasenv,term,term (* term non e' significativo *) in let flags = if fast then {AutoTypes.timeout = 1.0;maxwidth = 2;maxdepth = 2} else {AutoTypes.timeout = 1.0;maxwidth = 3;maxdepth = 3} in match auto flags proof context ?universe cache args_for_auto with | [],cache,universe -> raise (UnableToSaturate (Some universe,cache)) | substs,cache,universe -> List.map (fun subst -> let metasenv = CicMetaSubst.apply_subst_metasenv subst metasenv in let head = CicMetaSubst.apply_subst subst head in let newmetas = List.filter (fun (i,_,_) ->i >= oldnewmeta) metasenv in let args = List.map (CicMetaSubst.apply_subst subst) args in args,metasenv,newmetas,head,newmeta) substs, Some universe,cache in results,universe,cache ;; let rec is_an_equality = function | Cic.Appl [Cic.MutInd (uri, _, _); ty; t1; t2] when (LibraryObjects.is_eq_URI uri) -> true | Cic.Prod (name, s, t) -> is_an_equality t | _ -> false ;; let build_equality_of_hypothesis index head args newmetas = match head with | Cic.Appl [Cic.MutInd (uri, _, _); ty; t1; t2] -> let p = if args = [] then Cic.Rel index else Cic.Appl ((Cic.Rel index)::args) in debug_print (lazy (Printf.sprintf "OK: %s" (CicPp.ppterm p))); let o = !Utils.compare_terms t1 t2 in let stat = (ty,t1,t2,o) in (* let w = compute_equality_weight stat in *) let w = 0 in let proof = Equality.Exact p in Equality.mk_equality (w, proof, stat, newmetas) | _ -> assert false ;; let build_equality ty t1 t2 proof menv = let o = !Utils.compare_terms t1 t2 in let stat = (ty,t1,t2,o) in let w = compute_equality_weight stat in let proof = Equality.Exact proof in Equality.mk_equality (w, proof, stat, menv) ;; let find_equalities ?(auto = default_auto) context proof ?universe cache = prerr_endline "find_equalities"; let module C = Cic in let module S = CicSubstitution in let module T = CicTypeChecker in let _,metasenv,_,_ = proof in let newmeta = ProofEngineHelpers.new_meta_of_proof ~proof in let rec aux universe cache index newmeta = function | [] -> [], newmeta,universe,cache | (Some (_, C.Decl (term)))::tl -> debug_print (lazy (Printf.sprintf "Examining: %d (%s)" index (CicPp.ppterm term))); let do_find context term = match term with | C.Prod (name, s, t) when is_an_equality t -> (try let term = S.lift index term in let saturated ,universe,cache = saturate_term context metasenv newmeta term ?universe cache auto false in let eqs,newmeta = List.fold_left (fun (acc,newmeta) (args,metasenv,newmetas,head,newmeta') -> let equality = build_equality_of_hypothesis index head args newmetas in equality::acc,(max newmeta newmeta' + 1)) ([],0) saturated in eqs, newmeta, universe, cache with UnableToSaturate (universe,cache) -> [],newmeta,universe,cache) | C.Appl [C.MutInd (uri, _, _); ty; t1; t2] when LibraryObjects.is_eq_URI uri -> let term = S.lift index term in let e = build_equality_of_hypothesis index term [] [] in [e], (newmeta+1),universe,cache | _ -> [], newmeta, universe, cache in let eqs, newmeta, universe, cache = do_find context term in if eqs = [] then debug_print (lazy "skipped") else debug_print (lazy "ok"); let rest, newmeta,universe,cache = aux universe cache (index+1) newmeta tl in List.map (fun x -> index,x) eqs @ rest, newmeta, universe, cache | _::tl -> aux universe cache (index+1) newmeta tl in let il, maxm, universe, cache = aux universe cache 1 newmeta context in let indexes, equalities = List.split il in (* ignore (List.iter (check_eq context "find") equalities); *) indexes, equalities, maxm, universe, cache ;; (* let equations_blacklist = List.fold_left (fun s u -> UriManager.UriSet.add (UriManager.uri_of_string u) s) UriManager.UriSet.empty [ "cic:/Coq/Init/Logic/eq.ind#xpointer(1/1/1)"; "cic:/Coq/Init/Logic/trans_eq.con"; "cic:/Coq/Init/Logic/f_equal.con"; "cic:/Coq/Init/Logic/f_equal2.con"; "cic:/Coq/Init/Logic/f_equal3.con"; "cic:/Coq/Init/Logic/f_equal4.con"; "cic:/Coq/Init/Logic/f_equal5.con"; "cic:/Coq/Init/Logic/sym_eq.con"; "cic:/Coq/Init/Logic/eq_ind.con"; "cic:/Coq/Init/Logic/eq_ind_r.con"; "cic:/Coq/Init/Logic/eq_rec.con"; "cic:/Coq/Init/Logic/eq_rec_r.con"; "cic:/Coq/Init/Logic/eq_rect.con"; "cic:/Coq/Init/Logic/eq_rect_r.con"; "cic:/Coq/Logic/Eqdep/UIP.con"; "cic:/Coq/Logic/Eqdep/UIP_refl.con"; "cic:/Coq/Logic/Eqdep_dec/eq2eqT.con"; "cic:/Coq/ZArith/Zcompare/rename.con"; (* ALB !!!! questo e` imbrogliare, ma x ora lo lasciamo cosi`... perche' questo cacchio di teorema rompe le scatole :'( *) "cic:/Rocq/SUBST/comparith/mult_n_2.con"; "cic:/matita/logic/equality/eq_f.con"; "cic:/matita/logic/equality/eq_f2.con"; "cic:/matita/logic/equality/eq_rec.con"; "cic:/matita/logic/equality/eq_rect.con"; ] ;; *) let equations_blacklist = UriManager.UriSet.empty;; let tty_of_u u = (* let _ = <:start> in *) let t = CicUtil.term_of_uri u in let ty, _ = CicTypeChecker.type_of_aux' [] [] t CicUniv.empty_ugraph in (* let _ = <:stop> in *) t, ty ;; let utty_of_u u = let t,ty = tty_of_u u in u, t, ty ;; let find_library_equalities ?(auto = default_auto) caso_strano dbd context status maxmeta ?universe cache = prerr_endline "find_library_equalities"; let module C = Cic in let module S = CicSubstitution in let module T = CicTypeChecker in (* let _ = <:start> in *) let proof, goalno = status in let _,metasenv,_,_ = proof in let metano,context,ty = CicUtil.lookup_meta goalno metasenv in let signature = if caso_strano then begin match ty with | Cic.Appl[Cic.MutInd(u,0,_);eq_ty;l;r] -> (let mainl, sigl = MetadataConstraints.signature_of l in let mainr, sigr = MetadataConstraints.signature_of r in match mainl,mainr with | Some (uril,tyl), Some (urir, tyr) when LibraryObjects.is_eq_URI uril && LibraryObjects.is_eq_URI urir && tyl = tyr -> Some (mainl,MetadataConstraints.UriManagerSet.union sigl sigr) | _ -> let u = (UriManager.uri_of_string (UriManager.string_of_uri u ^ "#xpointer(1/1)")) in let main = Some (u, []) in Some (main,MetadataConstraints.UriManagerSet.union sigl sigr)) | _ -> assert false end else None in prerr_endline "find_library_equalities.1"; let eqs = MetadataQuery.equations_for_goal ~dbd ?signature status in (* let _ = <:stop> in *) prerr_endline "find_library_equalities.2"; let is_var_free (_,term,_ty) = let rec is_var_free = function | C.Var _ -> false | C.Appl l -> List.for_all is_var_free l | C.Prod (_, s, t) | C.Lambda (_, s, t) | C.LetIn (_, s, t) | C.Cast (s, t) -> (is_var_free s) && (is_var_free t) | _ -> true in is_var_free term in let is_monomorphic_eq (_,term,termty) = let rec last = function | Cic.Prod(_,_,t) -> last t | t -> t in match last termty with | C.Appl [C.MutInd (_, _, []); Cic.Rel _; _; _] -> false | C.Appl [C.MutInd (_, _, []); _; _; _] -> true | _ -> false in let candidates = List.map utty_of_u eqs in let candidates = List.filter is_monomorphic_eq candidates in let candidates = List.filter is_var_free candidates in let rec aux universe cache newmeta = function | [] -> [], newmeta, universe, cache | (uri, term, termty)::tl -> debug_print (lazy (Printf.sprintf "Examining: %s (%s)" (CicPp.ppterm term) (CicPp.ppterm termty))); let res, newmeta, universe, cache = match termty with | C.Prod (name, s, t) -> (try let saturated, universe,cache = saturate_term context metasenv newmeta termty ?universe cache auto true in let eqs,newmeta = List.fold_left (fun (acc,newmeta) (args,metasenv,newmetas,head,newmeta') -> match head with | C.Appl [C.MutInd (uri, _, _); ty; t1; t2] when LibraryObjects.is_eq_URI uri -> let proof = C.Appl (term::args) in prerr_endline ("PROVA: " ^ CicPp.ppterm proof); let equality = build_equality ty t1 t2 proof newmetas in equality::acc,(max newmeta newmeta' + 1) | _ -> assert false) ([],0) saturated in eqs, newmeta , universe, cache with UnableToSaturate (universe,cache) -> [], newmeta , universe, cache) | C.Appl [C.MutInd (uri, _, _); ty; t1; t2] -> let e = build_equality ty t1 t2 term [] in [e], (newmeta+1), universe, cache | _ -> assert false in let res = List.map (fun e -> uri, e) res in let tl, newmeta, universe, cache = aux universe cache newmeta tl in res @ tl, newmeta, universe, cache in let found, maxm, universe, cache = aux universe cache maxmeta candidates in let uriset, eqlist = let mceq = Equality.meta_convertibility_eq in (List.fold_left (fun (s, l) (u, e) -> if List.exists (mceq e) (List.map snd l) then ( debug_print (lazy (Printf.sprintf "NO!! %s already there!" (Equality.string_of_equality e))); (UriManager.UriSet.add u s, l) ) else (UriManager.UriSet.add u s, (u, e)::l)) (UriManager.UriSet.empty, []) found) in uriset, eqlist, maxm, universe, cache ;; let get_stats () = "" (*<:show>*) ;;