Section FuncEq.
Variables (A1 A2 A3 A4 A5 A6 A7 B : Type).
Proof using. intros. subst. auto. Qed.
Proof using. intros. subst. auto. Qed.
Proof using. intros. subst. auto. Qed.
Proof using. intros. subst. auto. Qed.
Proof using. intros. subst. auto. Qed.
Proof using. intros. subst. auto. Qed.
Proof using. intros. subst. auto. Qed.
End FuncEq.
Ltac fequal_post :=
try solve [ reflexivity ].
(** [fequal_support_for_exist], implemented in [LibEqual], is meant to simplify goals of the form [exist _ _ = exist _ _ ] and [existT _ _ = existT _ _], by exploiting proof irrelevance. *)
Ltac fequal_support_for_exist tt :=
fail.
(** For a n-ary tuple, [fequal], unlike [f_equal] enforces a recursive call on the (n-1)-ary tuple associated with the right component. *)
Ltac fequal_base :=
match goal with
| |- (_,_,_) = (_,_,_) =>  apply args_eq_2; [ fequal_base | ]
| |- _ => first
[ fequal_support_for_exist tt
| apply args_eq_1
| apply args_eq_2
| apply args_eq_3
| apply args_eq_4
| apply args_eq_5
| apply args_eq_6
| apply args_eq_7
| f_equal (* fallback to Coq [f_equal] *) ]
end.
Tactic Notation "fequal" :=
fequal_base; fequal_post.
(** [fequals] is the same as [fequal] except that it tries and solve all trivial subgoals, using [reflexivity] and [congruence] (as well as the proof-irrelevance principle). [fequals] applies to goals of the form [f x1 .. xN = f y1 .. yN] and produces some subgoals of the form [xi = yi]). *)
Ltac fequals_post :=
try solve [ reflexivity | congruence | apply proof_irrelevance ].
Tactic Notation "fequals" :=
fequal; fequals_post.
(** [fequals_rec] calls [fequals] recursively. It is equivalent to [repeat (progress fequals)]. *)
Tactic Notation "fequals_rec" :=
repeat (progress fequals).
(* ################################################################# *)
(** * Inversion *)
(* ================================================================= *)
(** ** Basic Inversion *)
(** [invert keep H] is same to [inversion H] except that it puts all the facts obtained in the goal. The keyword [keep] means that the hypothesis [H] should not be removed. *)
Tactic Notation "invert" "keep" hyp(H) :=
pose ltac_mark; inversion H; gen_until_mark.
(** [invert keep H as X1 .. XN] is the same as [inversion H as ...] except that only hypotheses which are not variable need to be named explicitely, in a similar fashion as [introv] is used to name only hypotheses. *)
Tactic Notation "invert" "keep" hyp(H) "as" simple_intropattern(I1) :=
invert keep H; introv I1.
Tactic Notation "invert" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
invert keep H; introv I1 I2.
Tactic Notation "invert" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
invert keep H; introv I1 I2 I3.
(** [invert H] is same to [inversion H] except that it puts all the facts obtained in the goal and clears hypothesis [H]. In other words, it is equivalent to [invert keep H; clear H]. *)
Tactic Notation "invert" hyp(H) :=
invert keep H; clear H.
(** [invert H as X1 .. XN] is the same as [invert keep H as X1 .. XN] but it also clears hypothesis [H]. *)
Tactic Notation "invert_tactic" hyp(H) tactic(tac) :=
let H' := fresh "TEMP" in rename H into H'; tac H'; clear H'.
Tactic Notation "invert" hyp(H) "as" simple_intropattern(I1) :=
invert_tactic H (fun H => invert keep H as I1).
Tactic Notation "invert" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
invert_tactic H (fun H => invert keep H as I1 I2).
Tactic Notation "invert" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
invert_tactic H (fun H => invert keep H as I1 I2 I3).
(* ================================================================= *)
(** ** Inversion with Substitution *)
(** Our inversion tactics is able to get rid of dependent equalities generated by [inversion], using proof irrelevance. *)
(* --we do not import Eqdep because it imports nasty hints automatically From TLC Require Import Eqdep. *)

Axiom inj_pair2 :  (* is in fact derivable from the axioms in LibAxiom.v *)
forall (U : Type) (P : U -> Type) (p : U) (x y : P p),
existT P p x = existT P p y -> x = y.