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/.
31 open HelmLibraryObjects
35 (** perform debugging output? *)
38 (** debugging print *)
41 prerr_endline ("RING WARNING: " ^ s)
46 Note: For constructors URIs aren't really URIs but rather triples of
47 the form (uri, typeno, consno). This discrepancy is to preserver an
48 uniformity of invocation of "mkXXX" functions.
51 let equality_is_a_congruence_A =
52 uri_of_string "cic:/Coq/Init/Logic/Logic_lemmas/equality/A.var"
53 let equality_is_a_congruence_x =
54 uri_of_string "cic:/Coq/Init/Logic/Logic_lemmas/equality/x.var"
55 let equality_is_a_congruence_y =
56 uri_of_string "cic:/Coq/Init/Logic/Logic_lemmas/equality/y.var"
59 uri_of_string "cic:/Coq/ring/Ring_abstract/apolynomial.ind"
60 let apvar_uri = (apolynomial_uri, 0, 1)
61 let ap0_uri = (apolynomial_uri, 0, 2)
62 let ap1_uri = (apolynomial_uri, 0, 3)
63 let applus_uri = (apolynomial_uri, 0, 4)
64 let apmult_uri = (apolynomial_uri, 0, 5)
65 let apopp_uri = (apolynomial_uri, 0, 6)
67 let quote_varmap_A_uri = uri_of_string "cic:/Coq/ring/Quote/variables_map/A.var"
68 let varmap_uri = uri_of_string "cic:/Coq/ring/Quote/varmap.ind"
69 let empty_vm_uri = (varmap_uri, 0, 1)
70 let node_vm_uri = (varmap_uri, 0, 2)
71 let varmap_find_uri = uri_of_string "cic:/Coq/ring/Quote/varmap_find.con"
72 let index_uri = uri_of_string "cic:/Coq/ring/Quote/index.ind"
73 let left_idx_uri = (index_uri, 0, 1)
74 let right_idx_uri = (index_uri, 0, 2)
75 let end_idx_uri = (index_uri, 0, 3)
77 let abstract_rings_A_uri =
78 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/A.var"
79 let abstract_rings_Aplus_uri =
80 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/Aplus.var"
81 let abstract_rings_Amult_uri =
82 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/Amult.var"
83 let abstract_rings_Aone_uri =
84 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/Aone.var"
85 let abstract_rings_Azero_uri =
86 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/Azero.var"
87 let abstract_rings_Aopp_uri =
88 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/Aopp.var"
89 let abstract_rings_Aeq_uri =
90 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/Aeq.var"
91 let abstract_rings_vm_uri =
92 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/vm.var"
93 let abstract_rings_T_uri =
94 uri_of_string "cic:/Coq/ring/Ring_abstract/abstract_rings/T.var"
95 let interp_ap_uri = uri_of_string "cic:/Coq/ring/Ring_abstract/interp_ap.con"
97 uri_of_string "cic:/Coq/ring/Ring_abstract/interp_sacs.con"
98 let apolynomial_normalize_uri =
99 uri_of_string "cic:/Coq/ring/Ring_abstract/apolynomial_normalize.con"
100 let apolynomial_normalize_ok_uri =
101 uri_of_string "cic:/Coq/ring/Ring_abstract/apolynomial_normalize_ok.con"
103 (** CIC PREDICATES *)
106 check whether a term is a constant or not, if argument "uri" is given and is
107 not "None" also check if the constant correspond to the given one or not
109 let cic_is_const ?(uri: uri option = None) term =
113 | Cic.Const _ -> true
117 | Cic.Const (u, _) when (eq u realuri) -> true
120 (** PROOF AND GOAL ACCESSORS *)
124 @return the uri of a given proof
126 let uri_of_proof ~proof:(uri, _, _, _) = uri
129 @param status current proof engine status
130 @raise Failure if proof is None
131 @return current goal's metasenv
133 let metasenv_of_status ((_,m,_,_), _) = m
136 @param status a proof engine status
137 @raise Failure when proof or goal are None
138 @return context corresponding to current goal
140 let context_of_status status =
141 let (proof, goal) = status in
142 let metasenv = metasenv_of_status status in
143 let _, context, _ = CicUtil.lookup_meta goal metasenv in
146 (** CIC TERM CONSTRUCTORS *)
149 Create a Cic term consisting of a constant
150 @param uri URI of the constant
152 @exp_named_subst explicit named substitution
154 let mkConst ~uri ~exp_named_subst =
155 Cic.Const (uri, exp_named_subst)
158 Create a Cic term consisting of a constructor
159 @param uri triple <uri, typeno, consno> where uri is the uri of an inductive
160 type, typeno is the type number in a mutind structure (0 based), consno is
161 the constructor number (1 based)
162 @exp_named_subst explicit named substitution
164 let mkCtor ~uri:(uri, typeno, consno) ~exp_named_subst =
165 Cic.MutConstruct (uri, typeno, consno, exp_named_subst)
168 Create a Cic term consisting of a type member of a mutual induction
169 @param uri pair <uri, typeno> where uri is the uri of a mutual inductive
170 type and typeno is the type number (0 based) in the mutual induction
171 @exp_named_subst explicit named substitution
173 let mkMutInd ~uri:(uri, typeno) ~exp_named_subst =
174 Cic.MutInd (uri, typeno, exp_named_subst)
179 raised when the current goal is not ringable; a goal is ringable when is an
180 equality on reals (@see r_uri)
182 exception GoalUnringable
184 (** RING's FUNCTIONS LIBRARY *)
187 Check whether the ring tactic can be applied on a given term (i.e. that is
188 an equality on reals)
189 @param term to be tested
190 @return true if the term is ringable, false otherwise
193 let is_equality = function
194 | Cic.MutInd (uri, 0, []) when (eq uri Logic.eq_URI) -> true
197 let is_real = function
198 | Cic.Const (uri, _) when (eq uri Reals.r_URI) -> true
202 | Cic.Appl (app::set::_::_::[]) when (is_equality app && is_real set) ->
203 warn "Goal Ringable!";
206 warn "Goal Not Ringable :-((";
210 split an equality goal of the form "t1 = t2" in its two subterms t1 and t2
211 after checking that the goal is ringable
212 @param goal the current goal
213 @return a pair (t1,t2) that are two sides of the equality goal
214 @raise GoalUnringable if the goal isn't ringable
216 let split_eq = function
217 | (Cic.Appl (_::_::t1::t2::[])) as term when ringable term ->
218 warn ("<term1>" ^ (CicPp.ppterm t1) ^ "</term1>");
219 warn ("<term2>" ^ (CicPp.ppterm t2) ^ "</term2>");
221 | _ -> raise GoalUnringable
224 @param i an integer index representing a 1 based number of node in a binary
225 search tree counted in a fbs manner (i.e.: 1 is the root, 2 is the left
226 child of the root (if any), 3 is the right child of the root (if any), 4 is
227 the left child of the left child of the root (if any), ....)
228 @param proof the current proof
229 @return an index representing the same node in a varmap (@see varmap_uri),
230 the returned index is as defined in index (@see index_uri)
233 let rec digits_of_int n =
234 if n=1 then [] else (n mod 2 = 1)::(digits_of_int (n lsr 1))
239 mkCtor (if (digit = true) then right_idx_uri else left_idx_uri) [];
241 (List.rev (digits_of_int n)) (* remove leading true (i.e. digit 1) *)
242 (mkCtor end_idx_uri [])
245 Build a variable map (@see varmap_uri) from a variables array.
246 A variable map is almost a binary tree so this function receiving a var list
247 like [v;w;x;y;z] will build a varmap of shape: v
252 @param vars variables array
253 @return a cic term representing the variable map containing vars variables
255 let btree_of_array ~vars =
257 let empty_vm_r = mkCtor empty_vm_uri [quote_varmap_A_uri,r] in
258 let node_vm_r = mkCtor node_vm_uri [quote_varmap_A_uri,r] in
259 let size = Array.length vars in
260 let halfsize = size lsr 1 in
261 let rec aux n = (* build the btree starting from position n *)
263 n is the position in the vars array _1_based_ in order to access
264 left and right child using (n*2, n*2+1) trick
268 else if n > halfsize then (* no more children *)
269 Cic.Appl [node_vm_r; vars.(n-1); empty_vm_r; empty_vm_r]
270 else (* still children *)
271 Cic.Appl [node_vm_r; vars.(n-1); aux (n*2); aux (n*2+1)]
276 abstraction function:
277 concrete polynoms -----> (abstract polynoms, varmap)
278 @param terms list of conrete polynoms
279 @return a pair <aterms, varmap> where aterms is a list of abstract polynoms
280 and varmap is the variable map needed to interpret them
282 let abstract_poly ~terms =
283 let varhash = Hashtbl.create 19 in (* vars hash, to speed up lookup *)
284 let varlist = ref [] in (* vars list in reverse order *)
285 let counter = ref 1 in (* index of next new variable *)
286 let rec aux = function (* TODO not tail recursive *)
287 (* "bop" -> binary operator | "uop" -> unary operator *)
288 | Cic.Appl (bop::t1::t2::[])
289 when (cic_is_const ~uri:(Some Reals.rplus_URI) bop) -> (* +. *)
290 Cic.Appl [mkCtor applus_uri []; aux t1; aux t2]
291 | Cic.Appl (bop::t1::t2::[])
292 when (cic_is_const ~uri:(Some Reals.rmult_URI) bop) -> (* *. *)
293 Cic.Appl [mkCtor apmult_uri []; aux t1; aux t2]
294 | Cic.Appl (uop::t::[])
295 when (cic_is_const ~uri:(Some Reals.ropp_URI) uop) -> (* ~-. *)
296 Cic.Appl [mkCtor apopp_uri []; aux t]
297 | t when (cic_is_const ~uri:(Some Reals.r0_URI) t) -> (* 0. *)
299 | t when (cic_is_const ~uri:(Some Reals.r1_URI) t) -> (* 1. *)
301 | t -> (* variable *)
303 Hashtbl.find varhash t (* use an old var *)
304 with Not_found -> begin (* create a new var *)
306 Cic.Appl [mkCtor apvar_uri []; path_of_int !counter]
309 varlist := t :: !varlist;
310 Hashtbl.add varhash t newvar;
314 let aterms = List.map aux terms in (* abstract vars *)
315 let varmap = (* build varmap *)
316 btree_of_array ~vars:(Array.of_list (List.rev !varlist))
321 given a list of abstract terms (i.e. apolynomials) build the ring "segments"
322 that is triples like (t', t'', t''') where
323 t' = interp_ap(varmap, at)
324 t'' = interp_sacs(varmap, (apolynomial_normalize at))
325 t''' = apolynomial_normalize_ok(varmap, at)
326 at is the abstract term built from t, t is a single member of aterms
328 let build_segments ~terms =
329 let theory_args_subst varmap =
330 [abstract_rings_A_uri, Reals.r ;
331 abstract_rings_Aplus_uri, Reals.rplus ;
332 abstract_rings_Amult_uri, Reals.rmult ;
333 abstract_rings_Aone_uri, Reals.r1 ;
334 abstract_rings_Azero_uri, Reals.r0 ;
335 abstract_rings_Aopp_uri, Reals.ropp ;
336 abstract_rings_vm_uri, varmap] in
337 let theory_args_subst' eq varmap t =
338 [abstract_rings_A_uri, Reals.r ;
339 abstract_rings_Aplus_uri, Reals.rplus ;
340 abstract_rings_Amult_uri, Reals.rmult ;
341 abstract_rings_Aone_uri, Reals.r1 ;
342 abstract_rings_Azero_uri, Reals.r0 ;
343 abstract_rings_Aopp_uri, Reals.ropp ;
344 abstract_rings_Aeq_uri, eq ;
345 abstract_rings_vm_uri, varmap ;
346 abstract_rings_T_uri, t] in
347 let interp_ap varmap =
348 mkConst interp_ap_uri (theory_args_subst varmap) in
349 let interp_sacs varmap =
350 mkConst interp_sacs_uri (theory_args_subst varmap) in
351 let apolynomial_normalize = mkConst apolynomial_normalize_uri [] in
352 let apolynomial_normalize_ok eq varmap t =
353 mkConst apolynomial_normalize_ok_uri (theory_args_subst' eq varmap t) in
354 let lxy_false = (** Cic funcion "fun (x,y):R -> false" *)
355 Cic.Lambda (Cic.Anonymous, Reals.r,
356 Cic.Lambda (Cic.Anonymous, Reals.r, Datatypes.falseb))
358 let (aterms, varmap) = abstract_poly ~terms in (* abstract polys *)
359 List.map (* build ring segments *)
361 Cic.Appl [interp_ap varmap ; t],
363 [interp_sacs varmap ; Cic.Appl [apolynomial_normalize; t]]),
364 Cic.Appl [apolynomial_normalize_ok lxy_false varmap Reals.rtheory ; t]
368 let status_of_single_goal_tactic_result =
370 proof,[goal] -> proof,goal
372 raise (Fail "status_of_single_goal_tactic_result: the tactic did not produce exactly a new goal")
374 (* Galla: spostata in variousTactics.ml
376 auxiliary tactic "elim_type"
377 @param status current proof engine status
378 @param term term to cut
380 let elim_type_tac ~term status =
381 warn "in Ring.elim_type_tac";
382 Tacticals.thens ~start:(cut_tac ~term)
383 ~continuations:[elim_simpl_intros_tac ~term:(Cic.Rel 1) ; Tacticals.id_tac] status
387 auxiliary tactic, use elim_type and try to close 2nd subgoal using proof
388 @param status current proof engine status
389 @param term term to cut
390 @param proof term used to prove second subgoal generated by elim_type
392 let elim_type2_tac ~term ~proof =
393 let elim_type2_tac ~term ~proof status =
394 let module E = EliminationTactics in
395 warn "in Ring.elim_type2";
396 ProofEngineTypes.apply_tactic
397 (Tacticals.thens ~start:(E.elim_type_tac ~term)
398 ~continuations:[Tacticals.id_tac ; exact_tac ~term:proof]) status
400 ProofEngineTypes.mk_tactic (elim_type2_tac ~term ~proof)
402 (* Galla: spostata in variousTactics.ml
404 Reflexivity tactic, try to solve current goal using "refl_eqT"
405 Warning: this isn't equale to the coq's Reflexivity because this one tries
406 only refl_eqT, coq's one also try "refl_equal"
407 @param status current proof engine status
409 let reflexivity_tac (proof, goal) =
410 warn "in Ring.reflexivity_tac";
411 let refl_eqt = mkCtor ~uri:refl_eqt_uri ~exp_named_subst:[] in
413 apply_tac (proof, goal) ~term:refl_eqt
414 with (Fail _) as e ->
415 let e_str = Printexc.to_string e in
416 raise (Fail ("Reflexivity failed with exception: " ^ e_str))
419 (** lift an 8-uple of debrujins indexes of n *)
420 let lift ~n (a,b,c,d,e,f,g,h) =
421 match (List.map (CicSubstitution.lift n) [a;b;c;d;e;f;g;h]) with
422 | [a;b;c;d;e;f;g;h] -> (a,b,c,d,e,f,g,h)
426 remove hypothesis from a given status starting from the last one
427 @param count number of hypotheses to remove
428 @param status current proof engine status
430 let purge_hyps_tac ~count =
431 let purge_hyps_tac ~count status =
432 let module S = ProofEngineStructuralRules in
433 let (proof, goal) = status in
434 let rec aux n context status =
436 match (n, context) with
440 (status_of_single_goal_tactic_result
441 (ProofEngineTypes.apply_tactic (S.clear ~hyp:hd) status))
442 | (_, []) -> failwith "Ring.purge_hyps_tac: no hypotheses left"
444 let (_, metasenv, _, _) = proof in
445 let (_, context, _) = CicUtil.lookup_meta goal metasenv in
446 let proof',goal' = aux count context status in
447 assert (goal = goal') ;
450 ProofEngineTypes.mk_tactic (purge_hyps_tac ~count)
455 Ring tactic, does associative and commutative rewritings in Reals ring
456 @param status current proof engine status
459 let ring_tac status =
460 let (proof, goal) = status in
461 warn "in Ring tactic";
462 let eqt = mkMutInd (Logic.eq_URI, 0) [] in
464 let metasenv = metasenv_of_status status in
465 let (metano, context, ty) = CicUtil.lookup_meta goal metasenv in
466 let (t1, t2) = split_eq ty in (* goal like t1 = t2 *)
467 match (build_segments ~terms:[t1; t2]) with
468 | (t1', t1'', t1'_eq_t1'')::(t2', t2'', t2'_eq_t2'')::[] -> begin
469 List.iter (* debugging, feel free to remove *)
470 (fun (descr, term) ->
471 warn (descr ^ " " ^ (CicPp.ppterm term)))
473 ["t1"; "t1'"; "t1''"; "t1'_eq_t1''";
474 "t2"; "t2'"; "t2''"; "t2'_eq_t2''"]
475 [t1; t1'; t1''; t1'_eq_t1'';
476 t2; t2'; t2''; t2'_eq_t2'']);
478 let new_hyps = ref 0 in (* number of new hypotheses created *)
479 ProofEngineTypes.apply_tactic
480 (Tacticals.try_tactics
482 "reflexivity", EqualityTactics.reflexivity_tac ;
483 "exact t1'_eq_t1''", exact_tac ~term:t1'_eq_t1'' ;
484 "exact t2'_eq_t2''", exact_tac ~term:t2'_eq_t2'' ;
485 "exact sym_eqt su t1 ...", exact_tac
488 [mkConst Logic.sym_eq_URI
489 [equality_is_a_congruence_A, Reals.r;
490 equality_is_a_congruence_x, t1'' ;
491 equality_is_a_congruence_y, t1
495 "elim_type eqt su t1 ...", ProofEngineTypes.mk_tactic (fun status ->
496 let status' = (* status after 1st elim_type use *)
497 let context = context_of_status status in
498 let b,_ = (*TASSI : FIXME*)
499 are_convertible context t1'' t1 CicUniv.empty_ugraph in
501 warn "t1'' and t1 are NOT CONVERTIBLE";
503 ProofEngineTypes.apply_tactic
504 (elim_type2_tac (* 1st elim_type use *)
506 ~term:(Cic.Appl [eqt; r; t1''; t1]))
509 incr new_hyps; (* elim_type add an hyp *)
511 (proof,[goal]) -> proof,goal
514 warn "t1'' and t1 are CONVERTIBLE";
518 let (t1,t1',t1'',t1'_eq_t1'',t2,t2',t2'',t2'_eq_t2'') =
519 lift 1 (t1,t1',t1'',t1'_eq_t1'', t2,t2',t2'',t2'_eq_t2'')
522 ProofEngineTypes.apply_tactic
523 (Tacticals.try_tactics (* try to solve 1st subgoal *)
525 "exact t2'_eq_t2''", exact_tac ~term:t2'_eq_t2'';
526 "exact sym_eqt su t2 ...",
530 [mkConst Logic.sym_eq_URI
531 [equality_is_a_congruence_A, Reals.r;
532 equality_is_a_congruence_x, t2'' ;
533 equality_is_a_congruence_y, t2
537 "elim_type eqt su t2 ...",
538 ProofEngineTypes.mk_tactic (fun status ->
540 let context = context_of_status status in
541 let b,_ = (* TASSI:FIXME *)
542 are_convertible context t2'' t2 CicUniv.empty_ugraph
545 warn "t2'' and t2 are NOT CONVERTIBLE";
547 ProofEngineTypes.apply_tactic
548 (elim_type2_tac (* 2nd elim_type use *)
550 ~term:(Cic.Appl [eqt; r; t2''; t2]))
553 incr new_hyps; (* elim_type add an hyp *)
555 (proof,[goal]) -> proof,goal
558 warn "t2'' and t2 are CONVERTIBLE";
562 try (* try to solve main goal *)
563 warn "trying reflexivity ....";
564 ProofEngineTypes.apply_tactic
565 EqualityTactics.reflexivity_tac status'
566 with (Fail _) -> (* leave conclusion to the user *)
567 warn "reflexivity failed, solution's left as an ex :-)";
568 ProofEngineTypes.apply_tactic
569 (purge_hyps_tac ~count:!new_hyps) status')])
575 raise (Fail ("Ring failure: " ^ s))
577 | _ -> (* impossible: we are applying ring exacty to 2 terms *)
580 (* wrap ring_tac catching GoalUnringable and raising Fail *)
582 let ring_tac status =
585 with GoalUnringable ->
586 raise (Fail "goal unringable")
588 let ring_tac = ProofEngineTypes.mk_tactic ring_tac