summaryrefslogtreecommitdiff
path: root/contrib/funind/tacinv.ml4
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/funind/tacinv.ml4')
-rw-r--r--contrib/funind/tacinv.ml4853
1 files changed, 853 insertions, 0 deletions
diff --git a/contrib/funind/tacinv.ml4 b/contrib/funind/tacinv.ml4
new file mode 100644
index 00000000..d2ae12d6
--- /dev/null
+++ b/contrib/funind/tacinv.ml4
@@ -0,0 +1,853 @@
+(*i camlp4deps: "parsing/grammar.cma" i*)
+
+(*s FunInv Tactic: inversion following the shape of a function. *)
+(* Use:
+ \begin{itemize}
+ \item The Tacinv directory must be in the path (-I <path> option)
+ \item use the bytecode version of coqtop or coqc (-byte option), or make a
+ coqtop
+ \item Do [Require Tacinv] to be able to use it.
+ \item For syntax see Tacinv.v
+ \end{itemize}
+*)
+
+
+(*i*)
+open Termops
+open Equality
+open Names
+open Pp
+open Tacmach
+open Proof_type
+open Tacinterp
+open Tactics
+open Tacticals
+open Term
+open Util
+open Printer
+open Reductionops
+open Inductiveops
+open Coqlib
+open Refine
+open Typing
+open Declare
+open Decl_kinds
+open Safe_typing
+open Vernacinterp
+open Evd
+open Environ
+open Entries
+open Setoid_replace
+open Tacinvutils
+(*i*)
+
+module Smap = Map.Make(struct type t = constr let compare = compare end)
+let smap_to_list m = Smap.fold (fun c cb l -> (c,cb)::l) m []
+let merge_smap m1 m2 = Smap.fold (fun c cb m -> Smap.add c cb m) m1 m2
+let rec listsuf i l = if i<=0 then l else listsuf (i-1) (List.tl l)
+let rec listpref i l = if i<=0 then [] else List.hd l :: listpref (i-1) (List.tl l)
+
+let mkthesort = mkProp (* would like to put Type here, but with which index? *)
+
+(* this is the prefix used to name equality hypothesis generated by
+ case analysis*)
+let equality_hyp_string = "_eg_"
+
+(* bug de refine: on doit ssavoir sur quelle hypothese on se trouve. valeur
+ initiale au debut de l'appel a la fonction proofPrinc: 1. *)
+let nthhyp = ref 1
+ (*debugging*)
+ (* let rewrules = ref [] *)
+ (*debugging*)
+let debug i = prstr ("DEBUG "^ string_of_int i ^"\n")
+let pr2constr = (fun c1 c2 -> prconstr c1; prstr " <---> "; prconstr c2)
+(* Operations on names *)
+let id_of_name = function
+ Anonymous -> id_of_string "H"
+ | Name id -> id;;
+let string_of_name nme = string_of_id (id_of_name nme)
+ (*end debugging *)
+
+let constr_of c = Constrintern.interp_constr Evd.empty (Global.env()) c
+
+let rec collect_cases l =
+ match l with
+ | [||] -> [||],[],[],[||],[||],[]
+ | arr ->
+ let (a,c,d,f,e,g)= arr.(0) in
+ let aa,lc,ld,_,_,_ =
+ collect_cases (Array.sub arr 1 ((Array.length arr)-1)) in
+ Array.append [|a|] aa , (c@lc) , (d@ld) , f , e, g
+
+let rec collect_pred l =
+ match l with
+ | [] -> [],[],[]
+ | (e1,e2,e3)::l' -> let a,b,c = collect_pred l' in (e1::a),(e2::b),(e3::c)
+
+
+(*s specific manipulations on constr *)
+let lift1_leqs leq=
+ List.map
+ (function (r,(typofg,g,d))
+ -> lift 1 r, (lift 1 typofg, lift 1 g , lift 1 d)) leq
+
+let lift1_relleqs leq= List.map (function (r,x) -> lift 1 r,x) leq
+
+(* WARNING: In the types, we don't lift the rels in the type. This is
+ intentional. Use with care. *)
+let lift1_lvars lvars= List.map
+ (function x,(nme,c) -> lift 1 x, (nme, (*lift 1*) c)) lvars
+
+let pop1_levar levars = List.map (function ev,tev -> ev, popn 1 tev) levars
+
+
+let rec add_n_dummy_prod t n =
+ if n<=0 then t
+ else add_n_dummy_prod (mkNamedProd (id_of_string "DUMMY") mkthesort t) (n-1)
+
+(* [add_lambdas t gl [csr1;csr2...]] returns [[x1:type of csr1]
+ [x2:type of csr2] t [csr <- x1 ...]], names of abstracted variables
+ are not specified *)
+let rec add_lambdas t gl lcsr =
+ match lcsr with
+ | [] -> t
+ | csr::lcsr' ->
+ let hyp_csr,hyptyp = csr,(pf_type_of gl csr) in
+ lambda_id hyp_csr hyptyp (add_lambdas t gl lcsr')
+
+(* [add_pis t gl [csr1;csr2...]] returns ([x1] :type of [csr1]
+ [x2]:type of csr2) [t]*)
+let rec add_pis t gl lcsr =
+ match lcsr with
+ | [] -> t
+ | csr::lcsr' ->
+ let hyp_csr,hyptyp = csr,(pf_type_of gl csr) in
+ prod_id hyp_csr hyptyp (add_pis t gl lcsr')
+
+let mkProdEg teq eql eqr concl =
+ mkProd (name_of_string "eg", mkEq teq eql eqr, lift 1 concl)
+
+let eqs_of_beqs x =
+ List.map (function (_,(a,b,c)) -> (Anonymous, mkEq a b c)) x
+
+
+let rec eqs_of_beqs_named_aux s i l =
+ match l with
+ | [] -> []
+ | (r,(a,b,c))::l' ->
+ (Name(id_of_string (s^ string_of_int i)), mkEq a b c)
+ ::eqs_of_beqs_named_aux s (i-1) l'
+
+
+let eqs_of_beqs_named s l = eqs_of_beqs_named_aux s (List.length l) l
+
+let rec patternify ltypes c nme =
+ match ltypes with
+ | [] -> c
+ | (mv,t)::ltypes' ->
+ let c'= substitterm 0 mv (mkRel 1) c in
+ let tlift = lift (List.length ltypes') t in
+ let res =
+ patternify ltypes' (mkLambda (newname_append nme "rec", tlift, c')) nme in
+ res
+
+let rec npatternify ltypes c =
+ match ltypes with
+ | [] -> c
+ | (mv,nme,t)::ltypes' ->
+ let c'= substitterm 0 mv (mkRel 1) c in
+(* let _ = prconstr c' in *)
+ let tlift = lift (List.length ltypes') t in
+ let res =
+ npatternify ltypes' (mkLambda (newname_append nme "", tlift, c')) in
+(* let _ = prconstr res in *)
+ res
+
+let rec apply_levars c lmetav =
+ match lmetav with
+ | [] -> [],c
+ | (i,typ) :: lmetav' ->
+ let levars,trm = apply_levars c lmetav' in
+ let exkey = mknewexist() in
+ ((exkey,typ)::levars), applistc trm [mkEvar exkey]
+ (* EXPERIMENT le refine est plus long si on met un cast:
+ ((exkey,typ)::levars), mkCast ((applistc trm [mkEvar exkey]),typ) *)
+
+
+let prod_change_concl c newconcl =
+ let lv,_ = decompose_prod c in prod_it newconcl lv
+
+let lam_change_concl c newconcl =
+ let lv,_ = decompose_prod c in lam_it newconcl lv
+
+
+let rec mkAppRel c largs n =
+ match largs with
+ | [] -> c
+ | arg::largs' ->
+ let newc = mkApp (c,[|(mkRel n)|]) in mkAppRel newc largs' (n-1)
+
+let applFull c typofc =
+ let lv,t = decompose_prod typofc in
+ let ltyp = List.map fst lv in
+ let res = mkAppRel c ltyp (List.length ltyp) in
+ res
+
+
+let rec build_rel_map typ type_of_b =
+ match (kind_of_term typ), (kind_of_term type_of_b) with
+ Evar _ , Evar _ -> Smap.empty
+ | Rel i, Rel j -> if i=j then Smap.empty
+ else Smap.add typ type_of_b Smap.empty
+ | Prod (name,c1,c2), Prod (nameb,c1b,c2b) ->
+ let map1 = build_rel_map c1 c1b in
+ let map2 = build_rel_map (pop c2) (pop c2b) in
+ merge_smap map1 map2
+ | App (f,args), App (fb,argsb) ->
+ (try build_rel_map_list (Array.to_list args) (Array.to_list argsb)
+ with Invalid_argument _ ->
+ failwith ("Could not generate case annotation. "^
+ "Two application with different length"))
+ | Const c1, Const c2 -> if c1=c2 then Smap.empty
+ else failwith ("Could not generate case annotation. "^
+ "Two different constants in a case annotation.")
+ | Ind c1, Ind c2 -> if c1=c2 then Smap.empty
+ else failwith ("Could not generate case annotation. "^
+ "Two different constants in a case annotation.")
+ | _,_ -> failwith ("Could not generate case annotation. "^
+ "Incompatibility between annotation and actual type")
+and build_rel_map_list ltyp ltype_of_b =
+ List.fold_left2 (fun a b c -> merge_smap a (build_rel_map b c))
+ Smap.empty ltyp ltype_of_b
+
+
+(*s Use (and proof) of the principle *)
+
+(*
+ \begin {itemize}
+ \item [concl] ([constr]): conclusions, cad (xi:ti)gl, ou gl est le but a
+ prouver, et xi:ti correspondent aux arguments donnés à la tactique. On
+ enlève un produit à chaque fois qu'on rencontre un binder, sans lift ou pop.
+ Initialement: une seule conclusion, puis specifique a chaque branche.
+ \item[absconcl] ([constr array]): les conclusions (un predicat pour chaque
+ fixp. mutuel) patternisées pour pouvoir être appliquées.
+ \item [mimick] ([constr]): le terme qu'on imite. On plonge dedans au fur et
+ à mesure, sans lift ni pop.
+ \item [nmefonc] ([constr array]): la constante correspondant à la fonction
+ appelée, permet de remplacer les appels recursifs par des appels à la
+ constante correspondante (non pertinent (et inutile) si on permet l'appel de
+ la tactique sur une terme donné directement (au lieu d'une constante comme
+ pour l'instant)).
+ \item [fonc] ([int*int]) : bornes des indices des variable correspondant aux
+ appels récursifs (plusieurs car fixp. mutuels), utile pour reconnaître les
+ appels récursifs (ATTENTION: initialement vide, reste vide tant qu'on n'est
+ pas dans un fix).
+ \end{itemize}
+*)
+
+type mimickinfo =
+ {
+ concl: constr;
+ absconcl: constr array;
+ mimick: constr;
+ env: env;
+ sigma: Evd.evar_map;
+ nmefonc: constr array;
+ fonc: int * int;
+ doeqs: bool; (* this reference is to toggle building of equalities during
+ the building of the principle (default is true) *)
+ fix: bool (* did I already went through a fix or case constr? lambdas
+ found before a case or a fix are treated as parameters of
+ the induction principle *)
+ }
+
+(*
+ \begin{itemize}
+ \item [lst_vars] ([(constr*(name*constr)) list]): liste des variables
+ rencontrées jusqu'à maintenant.
+ \item [lst_eqs] ([constr list]): liste d'équations engendrées au cours du
+ parcours, cette liste grandit à chaque case, et il faut lifter le tout à
+ chaque binder.
+ \item [lst_recs] ([constr list]): listes des appels récursifs rencontrés
+ jusque là.
+ \end{itemize}
+
+ Cette fonction rends un nuplet de la forme:
+
+ [t,
+ [(ev1,tev1);(ev2,tev2)..],
+ [(i1,j1,k1);(i2,j2,k2)..],
+ [|c1;c2..|],
+ [|typ1;typ2..|],
+ [(param,tparam)..]]
+
+ *)
+
+(* This could be the return type of [proofPrinc], but not yet *)
+type funind =
+ {
+ princ:constr;
+ evarlist: (constr*Term.types) list;
+ hypnum: (int*int*int) list;
+ mutfixmetas: constr array ;
+ conclarray: types array;
+ params:(constr*name*constr) list
+ }
+
+(*
+ où:
+
+ \begin{itemize}
+
+ \item[t] est le principe demandé, il contient des meta variables
+ représentant soit des trous à prouver plus tard, soit les conclusions à
+ compléter avant de rendre le terme (suivant qu'on utilise le principe pour
+ faire refine ou functional scheme). Il y plusieurs conclusions si plusieurs
+ fonction mutuellement récursives) voir la suite.
+
+ \item[[(ev1,tev1);(ev2,tev2)...]] est l'ensemble des méta variables
+ correspondant à des trous. [evi] est la meta variable, [tevi] est son type.
+
+ \item[(in,jn,kn)] sont les nombres respectivement de variables, d'équations,
+ et d'hypothèses de récurrence pour le but n. Permet de faire le bon nombre
+ d'intros et des rewrite au bons endroits dans la suite.
+
+ \item[[|c1;c2...|]] est un tableau de meta variables correspondant à chacun
+ des prédicats mutuellement récursifs construits.
+
+ \item[[|typ1;typ2...|]] est un tableau contenant les conclusions respectives
+ de chacun des prédicats mutuellement récursifs. Permet de finir la
+ construction du principe.
+
+ \item[[(param,tparam)..]] est la liste des paramètres (les lambda au-dessus
+ du fix) du fixpoint si fixpoint il y a.
+
+ \end{itemize}
+*)
+let heq_prefix = "H_eq_"
+
+type kind_of_hyp = Var | Eq (*| Rec*)
+
+let rec proofPrinc mi lst_vars lst_eqs lst_recs:
+ constr * (constr*Term.types) list * (int*int*int) list
+ * constr array * types array * (constr*name*constr) list =
+ match kind_of_term mi.mimick with
+ (* Fixpoint: we reproduce the Fix, fonc becomes (1,nbofmutf) to point on
+ the name of recursive calls *)
+ | Fix((iarr,i),(narr,tarr,carr)) ->
+
+ (* We construct the right predicates for each mutual fixpt *)
+ let rec build_pred n =
+ if n >= Array.length iarr then []
+ else
+ let ftyp = Array.get tarr n in
+ let gl = mknewmeta() in
+ let gl_app = applFull gl ftyp in
+ let pis = prod_change_concl ftyp gl_app in
+ let gl_abstr = lam_change_concl ftyp gl_app in
+ (gl,gl_abstr,pis):: build_pred (n+1) in
+
+ let evarl,predl,pisl = collect_pred (build_pred 0) in
+ let newabsconcl = Array.of_list predl in
+ let evararr = Array.of_list evarl in
+ let pisarr = Array.of_list pisl in
+ let newenv = push_rec_types (narr,tarr,carr) mi.env in
+
+ let rec collect_fix n =
+ if n >= Array.length iarr then [],[],[],[]
+ else
+ let nme = Array.get narr n in
+ let c = Array.get carr n in
+ (* rappelle sur le sous-terme, on ajoute un niveau de
+ profondeur (lift) parce que Fix est un binder. *)
+ let newmi = {mi with concl=(pisarr.(n)); absconcl=newabsconcl;
+ mimick=c; fonc=(1,((Array.length iarr)));env=newenv;fix=true} in
+ let appel_rec,levar,lposeq,_,evarrarr,parms =
+ proofPrinc newmi (lift1_lvars lst_vars)
+ (lift1_leqs lst_eqs) (lift1L lst_recs) in
+ let lnme,lappel_rec,llevar,llposeq = collect_fix (n+1) in
+ (nme::lnme),(appel_rec::lappel_rec),(levar@llevar), (lposeq@llposeq) in
+
+ let lnme,lappel_rec,llevar,llposeq =collect_fix 0 in
+ let lnme' = List.map (fun nme -> newname_append nme "_ind") lnme in
+ let anme = Array.of_list lnme' in
+ let aappel_rec = Array.of_list lappel_rec in
+ (* llevar are put outside the fix, so one level of rel must be removed *)
+ mkFix((iarr,i),(anme, pisarr,aappel_rec)),(pop1_levar llevar),llposeq,evararr,pisarr,[]
+
+ (* <pcase> Cases b of arrPt end.*)
+ | Case(cinfo, pcase, b, arrPt) ->
+
+ let prod_pcase,_ = decompose_lam pcase in
+ let nmeb,lastprod_pcase = List.hd prod_pcase in
+ let b'= apply_leqtrpl_t b lst_eqs in
+ let type_of_b = Typing.type_of mi.env mi.sigma b in
+ let new_lst_recs = lst_recs @ hdMatchSub_cpl b mi.fonc in
+ (* Replace the calls to the function (recursive calls) by calls to the
+ corresponding constant: *)
+ let d,f = mi.fonc in
+ let res = ref b' in
+ let _ = for i = d to f do
+ res := substitterm 0 (mkRel i) mi.nmefonc.(f-i) !res done in
+ let newb = !res in
+
+ (* [fold_proof t l n] rend le resultat de l'appel recursif sur les
+ elements de la liste l (correpsondant a arrPt), appele avec les bons
+ arguments: [concl] devient [(DUMMY1:t1;...;DUMMY:tn)concl'], ou [n]
+ est le nombre d'arguments du constructeur considéré (FIX: Hormis les
+ parametres!!), et [concl'] est concl ou l'on a réécrit [b] en ($c_n$
+ [rel1]...).*)
+
+ let rec fold_proof nth_construct eltPt' =
+ (* mise a jour de concl pour l'interieur du case, concl'= concl[b <- C x3
+ x2 x1... ], sans quoi les annotations ne sont plus coherentes *)
+ let cstr_appl,nargs = nth_dep_constructor type_of_b nth_construct in
+ let concl'' =
+ substitterm 0 (lift nargs b) cstr_appl (lift nargs mi.concl) in
+ let neweq = mkEq type_of_b newb (popn nargs cstr_appl) in
+ let concl_dummy = add_n_dummy_prod concl'' nargs in
+ let lsteqs_rew = apply_eq_leqtrpl lst_eqs neweq in
+ let new_lsteqs =
+ (mkRel (0-nargs),(type_of_b,newb, popn nargs cstr_appl))::lsteqs_rew in
+ let a',a'' = decompose_lam_n nargs eltPt' in
+ let newa'' =
+ if mi.doeqs
+ then mkLambda (name_of_string heq_prefix,lift nargs neweq,lift 1 a'')
+ else a'' in
+ let newmimick = lamn nargs a' newa'' in
+ let b',b'' = decompose_prod_n nargs concl_dummy in
+ let newb'' =
+ if mi.doeqs
+ then mkProd (name_of_string heq_prefix,lift nargs neweq,lift 1 b'')
+ else b'' in
+ let newconcl = prodn nargs b' newb'' in
+ let newmi = {mi with mimick=newmimick; concl=newconcl; fix=true} in
+ let a,b,c,d,e,p = proofPrinc newmi lst_vars new_lsteqs new_lst_recs in
+ a,b,c,d,e,p
+ in
+
+ let arrPt_proof,levar,lposeq,evararr,absc,_ =
+ collect_cases (Array.mapi fold_proof arrPt) in
+ let prod_pcase,concl_pcase = decompose_lam pcase in
+ let nme,typ = List.hd prod_pcase in
+ let suppllam_pcase = List.tl prod_pcase in
+ (* je remplace b par rel1 (apres avoir lifte un coup) dans la
+ future annotation du futur case: ensuite je mettrai un lambda devant *)
+ let typesofeqs' = eqs_of_beqs_named equality_hyp_string lst_eqs in
+ (* let typesofeqs = prod_it_lift typesofeqs' mi.concl in *)
+ let typesofeqs = mi.concl in
+ let typeof_case'' =
+ substitterm 0 (lift 1 b) (mkRel 1) (lift 1 typesofeqs) in
+
+ (* C'est un peu compliqué ici: en cas de type inductif vraiment dépendant
+ le piquant du case [pcase] contient des lambdas supplémentaires en tête
+ je les ai dans la variable [suppllam_pcase]. Le problème est que la
+ conclusion du piquant doit faire référence à ces variables plutôt qu'à
+ celle de l'exterieur. Ce qui suit permet de changer les reference de
+ newpacse' pour pointer vers les lambda du piquant. On procède comme
+ suit: on repère les rels qui pointent à l'interieur du piquant dans la
+ fonction imitée, pour ça on parcourt le dernier lambda du piquant (qui
+ contient le type de l'argument du case), et on remplace les rels
+ correspondant dans la preuve construite. *)
+
+ (* typ vient du piquant, type_of_b vient du typage de b.*)
+
+ let rel_smap =
+ if List.length suppllam_pcase=0 then Smap.empty else
+ build_rel_map (lift (List.length suppllam_pcase) type_of_b) typ in
+ let rel_map = smap_to_list rel_smap in
+ let rec substL l c =
+ match l with
+ [] -> c
+ | ((e,e') ::l') -> substL l' (substitterm 0 e (lift 1 e') c) in
+ let newpcase' = substL rel_map typeof_case'' in
+ let neweq = mkEq (lift (List.length suppllam_pcase + 1) type_of_b)
+ (lift (List.length suppllam_pcase + 1) newb) (mkRel 1) in
+ let newpcase =
+ if mi.doeqs then
+ mkProd (name_of_string "eg", neweq, lift 1 newpcase') else newpcase'
+ in
+ (* construction du dernier lambda du piquant. *)
+ let typeof_case' = mkLambda (newname_append nme "_ind" ,typ, newpcase) in
+ (* ajout des lambdas supplémentaires (type dépendant) du piquant. *)
+ let typeof_case =
+ lamn (List.length suppllam_pcase) suppllam_pcase typeof_case' in
+ let trm' = mkCase (cinfo,typeof_case,newb, arrPt_proof) in
+ let trm =
+ if mi.doeqs then mkApp (trm',[|(mkRefl type_of_b newb)|])
+ else trm' in
+ trm,levar,lposeq,evararr,absc,[] (* fix parms here (fix inside case)*)
+
+ | Lambda(nme, typ, cstr) ->
+ let _, _, cconcl = destProd mi.concl in
+ let d,f=mi.fonc in
+ let newenv = push_rel (nme,None,typ) mi.env in
+ let newmi = {mi with concl=cconcl; mimick=cstr; env=newenv;
+ fonc=((if d > 0 then d+1 else 0),(if f > 0 then f+1 else 0))} in
+ let newlst_var = (* if this lambda is a param, then don't add it here *)
+ if mi.fix then (mkRel 1,(nme,typ)) :: lift1_lvars lst_vars
+ else (*(mkRel 1,(nme,typ)) :: *) lift1_lvars lst_vars in
+ let rec_call,levar,lposeq,evararr,absc,parms =
+ proofPrinc newmi newlst_var (lift1_leqs lst_eqs) (lift1L lst_recs) in
+ (* are we inside a fixpoint or a case? then this is a normal lambda *)
+ if mi.fix then mkLambda (nme,typ,rec_call) , levar, lposeq,evararr,absc,[]
+ else (* otherwise this is a parameter *)
+ let metav = mknewmeta() in
+ let substmeta t = popn 1 (substitterm 0 (mkRel 1) metav t) in
+ let newrec_call = substmeta rec_call in
+ let newlevar = List.map (fun ev,tev -> ev, substmeta tev) levar in
+ let newabsc = Array.map substmeta absc in
+ newrec_call,newlevar,lposeq,evararr,newabsc,((metav,nme, typ)::parms)
+
+ | LetIn(nme,cstr1, typ, cstr) ->
+ failwith ("I don't deal with let ins yet. "^
+ "Please expand them before applying this function.")
+
+ | u ->
+ let varrels = List.rev (List.map fst lst_vars) in
+ let varnames = List.map snd lst_vars in
+ let nb_vars = (List.length varnames) in
+ let nb_eqs = (List.length lst_eqs) in
+ let eqrels = List.map fst lst_eqs in
+ (* [terms_recs]: appel rec du fixpoint, On concatène les appels recs
+ trouvés dans les let in et les Cases. *)
+ (* TODO: il faudra gérer plusieurs pt fixes imbriqués ? *)
+ let terms_recs = lst_recs @ (hdMatchSub_cpl mi.mimick mi.fonc) in
+
+ (*c construction du terme: application successive des variables, des
+ egalites et des appels rec, a la variable existentielle correspondant a
+ l'hypothese de recurrence en cours. *)
+ (* d'abord, on fabrique les types des appels recursifs en replacant le nom
+ de des fonctions par les predicats dans [terms_recs]: [(f_i t u v)]
+ devient [(P_i t u v)] *)
+ (* TODO optimiser ici: *)
+ let appsrecpred = exchange_reli_arrayi_L mi.absconcl mi.fonc terms_recs in
+ let typeofhole'' = prod_it_anonym_lift mi.concl appsrecpred in
+ let typeofhole = prodn nb_vars varnames typeofhole'' in
+
+ (* Un bug de refine m'oblige à mettre ici un H (meta variable à ce point,
+ mais remplacé par H avant le refine) au lieu d'un '?', je mettrai les
+ '?' à la fin comme ça [(([H1,H2,H3...] ...) ? ? ?)] *)
+
+ let newmeta = mknewmeta() in
+ let concl_with_var = applistc newmeta varrels in
+ let conclrecs = applistc concl_with_var terms_recs in
+ conclrecs,[newmeta,typeofhole], [nb_vars,(List.length terms_recs)
+ ,nb_eqs],[||],mi.absconcl,[]
+
+
+
+let mkevarmap_aux ex = let x,y = ex in (mkevarmap_from_listex x),y
+
+(* Interpretation of constr's *)
+let constr_of_Constr c = Constrintern.interp_constr Evd.empty (Global.env()) c
+
+
+(* TODO: deal with any term, not only a constant. *)
+let interp_fonc_tacarg fonctac gl =
+ (* [fonc] is the constr corresponding to fontact not unfolded,
+ if [fonctac] is a (qualified) name then this is a [const] ?. *)
+(* let fonc = constr_of_Constr fonctac in *)
+ (* TODO: replace the [with _ -> ] by something more precise in
+ the following. *)
+ (* [def_fonc] is the definition of fonc. TODO: We should do this only
+ if [fonc] is a const, and take [fonc] otherwise.*)
+ try fonctac, pf_const_value gl (destConst fonctac)
+ with _ -> failwith ("don't know how to deal with this function "
+ ^"(DEBUG:is it a constante?)")
+
+
+
+
+(* [invfun_proof fonc def_fonc gl_abstr pis] builds the principle,
+ following the shape of [def_fonc], [fonc] is the constant
+ corresponding to [def_func] (or a reduced form of it ?), gl_abstr and
+ pis are the goal to be proved, of the form [x,y...]g and (x.y...)g.
+
+ This function calls the big function proofPrinc. *)
+
+let invfun_proof fonc def_fonc gl_abstr pis env sigma =
+ let mi = {concl=pis; absconcl=gl_abstr; mimick=def_fonc; env=env;
+ sigma=sigma; nmefonc=fonc; fonc=(0,0); doeqs=true; fix=false} in
+ let princ_proof,levar,lposeq,evararr,absc,parms = proofPrinc mi [] [] [] in
+ princ_proof,levar,lposeq,evararr,absc,parms
+
+(* Do intros [i] times, then do rewrite on all introduced hyps which are called
+ like [heq_prefix], FIX: have another filter than the name. *)
+let rec iterintro i =
+ if i<=0 then tclIDTAC else
+ tclTHEN
+ (tclTHEN
+ intro
+ (iterintro (i-1)))
+ (fun gl ->
+ (tclREPEAT
+ (tclNTH_HYP i
+ (fun hyp ->
+ let hypname = (string_of_id (destVar hyp)) in
+ let sub =
+ try String.sub hypname 0 (String.length heq_prefix)
+ with _ -> "" (* different than [heq_prefix] *) in
+ if sub=heq_prefix then rewriteLR hyp else tclFAIL 0 "Cannot rewrite")
+ )) gl)
+
+
+(*
+ (fun hyp gl ->
+ let _ = print_string ("nthhyp= "^ string_of_int i) in
+ if isConst hyp && ((name_of_const hyp)==heq_prefix) then
+ let _ = print_string "YES\n" in
+ rewriteLR hyp gl
+ else
+ let _ = print_string "NO\n" in
+ tclIDTAC gl)
+ *)
+
+(* [invfun_basic C listargs_ids gl dorew lposeq] builds the tactic
+ which:
+ \begin{itemize}
+ \item Do refine on C (the induction principle),
+ \item try to Clear listargs_ids
+ \item if boolean dorew is true, then intro all new hypothesis, and
+ try rewrite on those hypothesis that are equalities.
+ \end{itemize}
+*)
+
+let invfun_basic open_princ_proof_applied listargs_ids gl dorew lposeq =
+ (tclTHEN_i
+ (tclTHEN
+ (tclTHEN
+ (* Refine on the right term (following the sheme of the
+ given function) *)
+ (fun gl -> refine open_princ_proof_applied gl)
+ (* Clear the hypothesis given as arguments of the tactic
+ (because they are generalized) *)
+ (tclTHEN simpl_in_concl (tclTRY (clear listargs_ids))))
+ (* Now we introduce the created hypothesis, and try rewrite on
+ equalities due to case analysis *)
+ (fun gl -> (tclIDTAC gl)))
+ (fun i gl ->
+ if not dorew then tclIDTAC gl
+ else
+ (* d,m,f correspond respectively to vars, induction hyps and
+ equalities*)
+ let d,m,f = List.nth lposeq (i-1) in
+ tclTHEN (iterintro (d)) (tclDO m (tclTRY intro)) gl)
+ )
+ gl
+
+
+
+
+(* This function trys to reduce instanciated arguments, provided they
+ are of the form [(C t u v...)] where [C] is a constructor, and
+ provided that the argument is not the argument of a fixpoint (i.e. the
+ argument corresponds to a simple lambda) . *)
+let rec applistc_iota cstr lcstr env sigma =
+ match lcstr with
+ | [] -> cstr,[]
+ | arg::lcstr' ->
+ let arghd =
+ if isApp arg then let x,_ = destApplication arg in x else arg in
+ if isConstruct arghd (* of the form [(C ...)]*)
+ then
+ applistc_iota (Tacred.nf env sigma (nf_beta (applistc cstr [arg])))
+ lcstr' env sigma
+ else
+ try
+ let nme,typ,suite = destLambda cstr in
+ let c, l = applistc_iota suite lcstr' env sigma in
+ mkLambda (nme,typ,c), arg::l
+ with _ -> cstr,arg::lcstr' (* the arg does not correspond to a lambda*)
+
+
+
+(* TODO: ne plus mettre les sous-but à l'exterieur, mais à l'intérieur (le bug
+ de refine est normalement resolu). Ca permettra 2 choses: d'une part que
+ les preuves soient plus simple, et d'autre part de fabriquer un terme de
+ refine qui pourra s'aapliquer SANS FAIRE LES INTROS AVANT, ce qui est bcp
+ mieux car fonctionne comme induction et plus comme inversion (pas de perte
+ de connexion entre les hypothèse et les variables). *)
+
+(*s Tactic that makes induction and case analysis following the shape
+ of a function (idf) given with arguments (listargs) *)
+let invfun c l dorew gl =
+(* \begin{itemize}
+ \item [fonc] = the constant corresponding to the function
+ (necessary for equalities of the form [(f x1 x2 ...)=...] where
+ [f] is the recursive function).
+ \item [def_fonc] = body of the function, where let ins have
+ been expanded. *)
+ let fonc, def_fonc' = interp_fonc_tacarg c gl in
+ let def_fonc'',listargs' =
+ applistc_iota def_fonc' l (pf_env gl) (project gl) in
+ let def_fonc = expand_letins def_fonc'' in
+ (* quantifies on previously generalized arguments.
+ [(x1:T1)...g[arg1 <- x1 ...]] *)
+ let pis = add_pis (pf_concl gl) gl listargs' in
+ (* princ_proof builds the principle *)
+ let _ = resetmeta() in
+ let princ_proof,levar, lposeq,evararr,_,parms =
+ invfun_proof [|fonc|] def_fonc [||] pis (pf_env gl) (project gl) in
+
+ (* Generalize the goal. [[x1:T1][x2:T2]... g[arg1 <- x1 ...]]. *)
+ let gl_abstr' = add_lambdas (pf_concl gl) gl listargs' in
+ (* apply parameters immediately *)
+ let gl_abstr = applistc gl_abstr' (List.map (fun x,y,z -> x) (List.rev parms)) in
+
+ (* we apply args of the fix now, the parameters will be applied later *)
+ let princ_proof_applied_args =
+ applistc princ_proof (listsuf (List.length parms) listargs') in
+
+ (* parameters are still there so patternify must not take them -> lift *)
+ let princ_proof_applied_lift =
+ lift (List.length levar) princ_proof_applied_args in
+
+ let princ_applied_hyps'' = patternify (List.rev levar)
+ princ_proof_applied_lift (Name (id_of_string "Hyp")) in
+ (* if there was a fix, we will not add "Q" as in funscheme, so we make a pop,
+ TODO: find were we made the lift in proofPrinc instead and supress it here,
+ and add lift in funscheme. *)
+ let princ_applied_hyps' =
+ if Array.length evararr > 0 then popn 1 princ_applied_hyps''
+ else princ_applied_hyps'' in
+
+ let princ_applied_hyps =
+ if Array.length evararr > 0 then (* mutual Fixpoint not treated in the tactic *)
+ (substit_red 0 (evararr.(0)) gl_abstr princ_applied_hyps')
+ else princ_applied_hyps' (* No Fixpoint *) in
+ let _ = prNamedConstr "princ_applied_hyps" princ_applied_hyps in
+
+ (* replace params metavar by real args *)
+ let rec replace_parms lparms largs t =
+ match lparms, largs with
+ [], _ -> t
+ | ((p,_,_)::lp), (a::la) -> let t'= substitterm 0 p a t in replace_parms lp la t'
+ | _, _ -> error "problem with number of args." in
+ let princ_proof_applied = replace_parms parms listargs' princ_applied_hyps in
+
+
+(*
+ (* replace params metavar by abstracted variables *)
+ let princ_proof_params = npatternify (List.rev parms) princ_applied_hyps in
+ (* we apply now the real parameters *)
+ let princ_proof_applied =
+ applistc princ_proof_params (listpref (List.length parms) listargs') in
+*)
+
+
+
+ let princ_applied_evars = apply_levars princ_proof_applied levar in
+ let open_princ_proof_applied = princ_applied_evars in
+ let listargs_ids = List.map destVar (List.filter isVar listargs') in
+ invfun_basic (mkevarmap_aux open_princ_proof_applied) listargs_ids
+ gl dorew lposeq
+
+(* function must be a constant, all arguments must be given. *)
+let invfun_verif c l dorew gl =
+ if not (isConst c) then error "given function is not a constant"
+ else
+ let x,_ = decompose_prod (pf_type_of gl c) in
+ if List.length x = List.length l then
+ try invfun c l dorew gl
+ with
+ UserError (x,y) -> raise (UserError (x,y))
+ else error "wrong number of arguments for the function"
+
+
+TACTIC EXTEND FunctionalInduction
+ [ "Functional" "Induction" constr(c) ne_constr_list(l) ]
+ -> [ invfun_verif c l true ]
+END
+
+
+
+(* Construction of the functional scheme. *)
+let buildFunscheme fonc mutflist =
+ let def_fonc = expand_letins (def_of_const fonc) in
+ let ftyp = type_of (Global.env ()) Evd.empty fonc in
+ let _ = resetmeta() in
+ let gl = mknewmeta() in
+ let gl_app = applFull gl ftyp in
+ let pis = prod_change_concl ftyp gl_app in
+ (* Here we call the function invfun_proof, that effectively
+ builds the scheme *)
+ let princ_proof,levar,_,evararr,absc,parms =
+ invfun_proof mutflist def_fonc [||] pis (Global.env()) Evd.empty in
+ (* parameters are still there (unboud rel), and patternify must not take them
+ -> lift*)
+ let princ_proof_lift = lift (List.length levar) princ_proof in
+ let princ_proof_hyps =
+ patternify (List.rev levar) princ_proof_lift (Name (id_of_string "Hyp")) in
+ let rec princ_replace_metas ev abs i t =
+ if i>= Array.length ev then t
+ else (* fix? *)
+ princ_replace_metas ev abs (i+1)
+ (mkLambda (
+ (Name (id_of_string ("Q"^(string_of_int i)))),
+ prod_change_concl (lift 0 abs.(i)) mkthesort,
+ (substitterm 0 ev.(i) (mkRel 1) (lift 0 t))))
+ in
+ let rec princ_replace_params params t =
+ List.fold_left (
+ fun acc ev,nam,typ ->
+ mkLambda (Name (id_of_name nam) , typ,
+ substitterm 0 ev (mkRel 1) (lift 0 acc)))
+ t (List.rev params) in
+ if Array.length evararr = 0 (* Is there a Fixpoint? *)
+ then (* No Fixpoint *)
+ princ_replace_params parms (mkLambda ((Name (id_of_string "Q")),
+ prod_change_concl ftyp mkthesort,
+ (substitterm 0 gl (mkRel 1) princ_proof_hyps)))
+ else (* there is a fix -> add parameters + replace metas *)
+ let princ_rpl = princ_replace_metas evararr absc 0 princ_proof_hyps in
+ princ_replace_params parms princ_rpl
+
+
+
+(* Declaration of the functional scheme. *)
+let declareFunScheme f fname mutflist =
+ let scheme =
+ buildFunscheme (constr_of f)
+ (Array.of_list (List.map constr_of (f::mutflist))) in
+ let _ = prstr "Principe:" in
+ let _ = prconstr scheme in
+ let ce = {
+ const_entry_body = scheme;
+ const_entry_type = None;
+ const_entry_opaque = false } in
+ let _= ignore (declare_constant fname (DefinitionEntry ce,IsDefinition)) in
+ ()
+
+
+
+VERNAC COMMAND EXTEND FunctionalScheme
+ [ "Functional" "Scheme" ident(na) ":=" "Induction" "for"
+ constr(c) "with" ne_constr_list(l) ]
+ -> [ declareFunScheme c na l ]
+| [ "Functional" "Scheme" ident(na) ":=" "Induction" "for" constr(c) ]
+ -> [ declareFunScheme c na [] ]
+END
+
+
+
+
+
+(*
+*** Local Variables: ***
+*** compile-command: "make -C ../.. contrib/funind/tacinv.cmo" ***
+*** tab-width: 1 ***
+*** tuareg-default-indent:1 ***
+*** tuareg-begin-indent:1 ***
+*** tuareg-let-indent:1 ***
+*** tuareg-match-indent:-1 ***
+*** tuareg-try-indent:1 ***
+*** tuareg-with-indent:1 ***
+*** tuareg-if-then-else-inden:1 ***
+*** fill-column: 78 ***
+*** indent-tabs-mode: nil ***
+*** test-tactic: "../../bin/coqtop -translate -q -batch -load-vernac-source ../../test-suite/success/Funind.v" ***
+*** End: ***
+*)
+
+