1 (* Copyright (C) 2002, HELM Team.
3 * This file is part of HELM, an Hypertextual, Electronic
4 * Library of Mathematics, developed at the Computer Science
5 * Department, University of Bologna, Italy.
7 * HELM is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * HELM is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with HELM; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * For details, see the HELM World-Wide-Web page,
23 * http://cs.unibo.it/helm/.
28 let debug_print = fun _ -> ()
30 (* term ha tipo t1=t2; funziona solo se t1 e t2 hanno in testa costruttori
33 let discriminate_tac ~term =
35 let module U = UriManager in
36 let module P = PrimitiveTactics in
37 let module T = Tacticals in
39 match LibraryObjects.true_URI () with
41 | None -> raise (ProofEngineTypes.Fail (lazy "You need to register the default \"true\" definition first. Please use the \"default\" command")) in
43 match LibraryObjects.false_URI () with
45 | None -> raise (ProofEngineTypes.Fail (lazy "You need to register the default \"false\" definition first. Please use the \"default\" command")) in
46 let fail msg = raise (ProofEngineTypes.Fail (lazy ("Discriminate: " ^ msg))) in
47 let find_discriminating_consno t1 t2 =
50 | C.MutConstruct _, C.MutConstruct _ when t1 = t2 -> None
51 | C.Appl ((C.MutConstruct _ as constr1) :: args1),
52 C.Appl ((C.MutConstruct _ as constr2) :: args2)
53 when constr1 = constr2 ->
54 let rec aux_list l1 l2 =
57 | hd1 :: tl1, hd2 :: tl2 ->
58 (match aux hd1 hd2 with
59 | None -> aux_list tl1 tl2
60 | Some _ as res -> res)
61 | _ -> (* same constructor applied to a different number of args *)
65 | ((C.MutConstruct (_,_,consno1,subst1)),
66 (C.MutConstruct (_,_,consno2,subst2)))
67 | ((C.MutConstruct (_,_,consno1,subst1)),
68 (C.Appl ((C.MutConstruct (_,_,consno2,subst2)) :: _)))
69 | ((C.Appl ((C.MutConstruct (_,_,consno1,subst1)) :: _)),
70 (C.MutConstruct (_,_,consno2,subst2)))
71 | ((C.Appl ((C.MutConstruct (_,_,consno1,subst1)) :: _)),
72 (C.Appl ((C.MutConstruct (_,_,consno2,subst2)) :: _)))
73 when (consno1 <> consno2) || (subst1 <> subst2) ->
75 | _ -> fail "not a discriminable equality"
79 let mk_branches_and_outtype turi typeno consno context args =
80 (* a list of "True" except for the element in position consno which
82 match fst (CicEnvironment.get_obj CicUniv.empty_ugraph turi) with
83 | C.InductiveDefinition (ind_type_list,_,paramsno,_) ->
84 let _,_,rty,constructor_list = List.nth ind_type_list typeno in
85 let false_constr_id,_ = List.nth constructor_list (consno - 1) in
89 (* dubbio: e' corretto ridurre in questo context ??? *)
90 let red_ty = CicReduction.whd context cty in
93 | C.Prod (_,_,target) when (k <= paramsno) ->
94 CicSubstitution.subst (List.nth args (k-1))
96 | C.Prod (binder,source,target) when (k > paramsno) ->
97 C.Lambda (binder, source, (aux target (k+1)))
99 if (id = false_constr_id)
100 then (C.MutInd(false_URI,0,[]))
101 else (C.MutInd(true_URI,0,[]))
103 (CicSubstitution.lift 1 (aux red_ty 1)))
107 let rec mk_lambdas rev_left_args =
109 0, args, C.Prod (_,so,ta) ->
111 (C.Name (incr seed; "x" ^ string_of_int !seed),
113 mk_lambdas rev_left_args (0,args,ta))
114 | 0, args, C.Sort _ ->
118 | n -> C.Rel n :: mk_rels (n - 1) in
119 let argsno = List.length args in
122 (if argsno + List.length rev_left_args > 0 then
124 (C.MutInd (turi, typeno, []) ::
126 (CicSubstitution.lift (argsno + 1))
127 (List.rev rev_left_args)) @
130 C.MutInd (turi,typeno,[])),
132 | 0, _, _ -> assert false (* seriously screwed up *)
133 | n, he::tl, C.Prod (_,_,ta) ->
134 mk_lambdas (he::rev_left_args)(n-1,tl,CicSubstitution.subst he ta)
136 assert false (* we should probably reduce in some context *)
138 mk_lambdas [] (paramsno, args, rty)
143 let discriminate'_tac ~term status =
144 let (proof, goal) = status in
145 let _,metasenv,_,_, _ = proof in
146 let _,context,_ = CicUtil.lookup_meta goal metasenv in
148 CicTypeChecker.type_of_aux' metasenv context term CicUniv.empty_ugraph
151 | C.Appl [(C.MutInd (equri, 0, [])) ; tty ; t1 ; t2]
152 when LibraryObjects.is_eq_URI equri ->
153 let turi,typeno,exp_named_subst,args =
155 | (C.MutInd (turi,typeno,exp_named_subst)) ->
156 turi,typeno,exp_named_subst,[]
157 | (C.Appl (C.MutInd (turi,typeno,exp_named_subst)::args)) ->
158 turi,typeno,exp_named_subst,args
159 | _ -> fail "not a discriminable equality"
162 match find_discriminating_consno t1 t2 with
163 | Some consno -> consno
164 | None -> fail "discriminating terms are structurally equal"
166 let branches,outtype =
167 mk_branches_and_outtype turi typeno consno context args
169 ProofEngineTypes.apply_tactic
171 ~start:(EliminationTactics.elim_type_tac (C.MutInd (false_URI, 0, [])))
175 (ReductionTactics.change_tac
176 ~pattern:(ProofEngineTypes.conclusion_pattern None)
179 C.Lambda ( C.Name "x", tty,
180 C.MutCase (turi, typeno, outtype, (C.Rel 1), branches));
186 (EqualityTactics.rewrite_simpl_tac
187 ~direction:`RightToLeft
188 ~pattern:(ProofEngineTypes.conclusion_pattern None)
191 (IntroductionTactics.constructor_tac ~n:1)))) status
192 | _ -> fail "not an equality"
194 ProofEngineTypes.mk_tactic (discriminate'_tac ~term)
197 let rec injection_tac ~first_time ~term ~liftno ~continuation =
198 let injection_tac ~term status =
199 let (proof, goal) = status in
200 let module C = Cic in
201 let module U = UriManager in
202 let module P = PrimitiveTactics in
203 let module T = Tacticals in
204 let _,metasenv,_,_, _ = proof in
205 let _,context,_ = CicUtil.lookup_meta goal metasenv in
206 let term = CicSubstitution.lift liftno term in
207 let termty,_ = (* TASSI: FIXME *)
208 CicTypeChecker.type_of_aux' metasenv context term CicUniv.empty_ugraph
210 ProofEngineTypes.apply_tactic
212 (C.Appl [(C.MutInd (equri, 0, [])) ; tty ; t1 ; t2])
213 when LibraryObjects.is_eq_URI equri ->
216 (C.MutInd (turi,typeno,exp_named_subst))
217 | (C.Appl (C.MutInd (turi,typeno,exp_named_subst)::_)) ->
220 ((C.MutConstruct (uri1,typeno1,consno1,exp_named_subst1)),
221 (C.MutConstruct (uri2,typeno2,consno2,exp_named_subst2)))
222 when (uri1 = uri2) && (typeno1 = typeno2) &&
223 (consno1 = consno2) &&
224 (exp_named_subst1 = exp_named_subst2)
228 (ProofEngineTypes.Fail (lazy "Injection: nothing to do"))
233 (uri1,typeno1,consno1,exp_named_subst1))::applist1),
236 (uri2,typeno2,consno2,exp_named_subst2))::applist2)
237 when (uri1 = uri2) && (typeno1 = typeno2) &&
238 (consno1 = consno2) &&
239 (exp_named_subst1 = exp_named_subst2)
241 let rec traverse_list i l1 l2 =
249 (match List.nth context (n-1) with
250 Some (C.Name id,_) ->
254 (ProofEngineStructuralRules.clear
256 ~continuation:(continuation ~liftno)
259 | hd1::tl1,hd2::tl2 ->
262 (CicReduction.are_convertible ~metasenv
263 context hd1 hd2 CicUniv.empty_ugraph)
265 traverse_list (i+1) tl1 tl2
267 injection1_tac ~i ~term
268 ~continuation:(traverse_list (i+1) tl1 tl2)
269 | _ -> assert false (* i 2 termini hanno in testa lo stesso costruttore, ma applicato a un numero diverso di termini *)
271 traverse_list 1 applist1 applist2 ~liftno
272 | ((C.MutConstruct (uri1,typeno1,consno1,exp_named_subst1)),
273 (C.MutConstruct (uri2,typeno2,consno2,exp_named_subst2)))
274 | ((C.MutConstruct (uri1,typeno1,consno1,exp_named_subst1)),
275 (C.Appl ((C.MutConstruct (uri2,typeno2,consno2,exp_named_subst2))::_)))
276 | ((C.Appl ((C.MutConstruct (uri1,typeno1,consno1,exp_named_subst1))::_)),
277 (C.MutConstruct (uri2,typeno2,consno2,exp_named_subst2)))
278 | ((C.Appl ((C.MutConstruct (uri1,typeno1,consno1,exp_named_subst1))::_)),
279 (C.Appl ((C.MutConstruct (uri2,typeno2,consno2,exp_named_subst2))::_)))
280 when (consno1 <> consno2) || (exp_named_subst1 <> exp_named_subst2) ->
281 discriminate_tac ~term
282 (*raise (ProofEngineTypes.Fail (lazy "Injection: not a projectable equality but a discriminable one"))*)
285 raise (ProofEngineTypes.Fail (lazy "Injection: not a projectable equality"))
291 raise (ProofEngineTypes.Fail (lazy "Injection: not a projectable equality"))
295 | _ -> raise (ProofEngineTypes.Fail (lazy "Injection: not an equation"))
298 ProofEngineTypes.mk_tactic (injection_tac ~term)
300 and injection1_tac ~term ~i ~liftno ~continuation =
301 let injection1_tac ~term ~i status =
302 let (proof, goal) = status in
303 (* precondizione: t1 e t2 hanno in testa lo stesso costruttore ma differiscono (o potrebbero differire?) nell'i-esimo parametro del costruttore *)
304 let module C = Cic in
305 let module S = CicSubstitution in
306 let module U = UriManager in
307 let module P = PrimitiveTactics in
308 let module T = Tacticals in
309 let term = CicSubstitution.lift liftno term in
310 let _,metasenv,_,_, _ = proof in
311 let _,context,_ = CicUtil.lookup_meta goal metasenv in
312 let termty,_ = (* TASSI: FIXME *)
313 CicTypeChecker.type_of_aux' metasenv context term CicUniv.empty_ugraph
315 match termty with (* an equality *)
316 (C.Appl [(C.MutInd (equri, 0, [])) ; tty ; t1 ; t2])
317 when LibraryObjects.is_eq_URI equri -> (
318 match tty with (* some inductive type *)
319 (C.MutInd (turi,typeno,exp_named_subst))
320 | (C.Appl (C.MutInd (turi,typeno,exp_named_subst)::_)) ->
321 let t1',t2',consno = (* sono i due sottotermini che differiscono *)
323 ((C.Appl ((C.MutConstruct (uri1,typeno1,consno1,exp_named_subst1))::applist1)),
324 (C.Appl ((C.MutConstruct (uri2,typeno2,consno2,exp_named_subst2))::applist2)))
325 when (uri1 = uri2) && (typeno1 = typeno2) && (consno1 = consno2) && (exp_named_subst1 = exp_named_subst2) -> (* controllo ridondante *)
326 (List.nth applist1 (i-1)),(List.nth applist2 (i-1)),consno2
330 CicTypeChecker.type_of_aux' metasenv context t1'
331 CicUniv.empty_ugraph in
332 let patterns,outtype =
334 fst (CicEnvironment.get_obj CicUniv.empty_ugraph turi)
336 C.InductiveDefinition (ind_type_list,_,paramsno,_)->
337 let _,_,_,constructor_list =
338 List.nth ind_type_list typeno in
340 List.nth constructor_list (consno - 1) in
344 (function (id,cty) ->
345 let reduced_cty = CicReduction.whd context cty in
348 C.Prod (_,_,target) when k <= paramsno ->
350 | C.Prod (binder,source,target) when k > paramsno ->
356 (incr seed; "y" ^ string_of_int !seed)
358 C.Lambda (binder',source,(aux target (k+1)))
360 let nr_param_constr = k - 1 - paramsno in
363 else S.lift (nr_param_constr + 1) t1' (* + 1 per liftare anche il lambda aggiunto esternamente al case *)
365 ) constructor_list in
368 let rec to_lambdas te head =
369 match CicReduction.whd context te with
370 | C.Prod (binder,so,ta) ->
375 C.Name (incr seed; "d" ^ string_of_int !seed)
377 C.Lambda (binder',so,to_lambdas ta head)
379 let rec skip_prods n te =
380 match n, CicReduction.whd context te with
382 | n, C.Prod (_,_,ta) -> skip_prods (n - 1) ta
383 | _, _ -> assert false
386 match CicSubstitution.lift (paramsno + 1) tty with
387 C.MutInd _ as tty' -> tty'
390 HExtlib.split_nth (paramsno +1) l in
394 | n -> C.Rel n :: (mk_rels (n - 1))
396 C.Appl (keep@mk_rels (List.length abstract))
399 match ind_type_list with
402 to_lambdas (skip_prods paramsno ty)
403 (C.Lambda (C.Name "x", abstracted_tty,
404 S.lift (2+paramsno) tty'))
407 | _ -> raise (ProofEngineTypes.Fail (lazy "Discriminate: object is not an Inductive Definition: it's imposible"))
409 ProofEngineTypes.apply_tactic
413 (C.Appl [C.MutInd (equri,0,[]) ; tty' ; t1' ; t2']))
415 [ injection_tac ~first_time:false ~liftno:0
417 (* here I need to lift all the continuations by 1;
418 since I am setting back liftno to 0, I actually
419 need to lift all the continuations by liftno + 1 *)
422 continuation ~liftno:(liftno + 1 + x)) ;
424 ~start:(ProofEngineTypes.mk_tactic
426 let (proof, goal) = status in
427 let _,metasenv,_,_, _ = proof in
429 CicUtil.lookup_meta goal metasenv
433 (C.Appl (C.MutInd (_,_,_)::arglist)) ->
437 (ProofEngineTypes.Fail
439 "Injection: goal after cut is not correct"))
441 ProofEngineTypes.apply_tactic
442 (ReductionTactics.change_tac
443 ~pattern:(ProofEngineTypes.conclusion_pattern
451 (turi,typeno,outtype,C.Rel 1,patterns)) ;
459 (EqualityTactics.rewrite_simpl_tac
460 ~direction:`LeftToRight
461 ~pattern:(ProofEngineTypes.conclusion_pattern None)
463 ~continuation:EqualityTactics.reflexivity_tac)
466 | _ -> raise (ProofEngineTypes.Fail (lazy "Injection: not an equality over elements of an inductive type"))
468 | _ -> raise (ProofEngineTypes.Fail (lazy "Injection: not an equality"))
470 ProofEngineTypes.mk_tactic (injection1_tac ~term ~i)
473 (* destruct performs either injection or discriminate *)
474 (* equivalent to Coq's "analyze equality" *)
477 ~first_time:true ~liftno:0 ~continuation:(fun ~liftno -> Tacticals.id_tac)