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. *)
(* Proof using. apply Eqdep.EqdepTheory.inj_pair2. Qed. *)
Ltac inverts_tactic H i1 i2 i3 i4 i5 i6 :=
let rec go i1 i2 i3 i4 i5 i6 :=
match goal with
| |- (ltac_Mark -> _) => intros _
| |- (?x = ?y -> _) => let H := fresh "TEMP" in intro H;
first [ subst x | subst y ];
go i1 i2 i3 i4 i5 i6
| |- (existT ?P ?p ?x = existT ?P ?p ?y -> _) =>
let H := fresh "TEMP" in intro H;
generalize (@inj_pair2 _ P p x y H);
clear H; go i1 i2 i3 i4 i5 i6
| |- (?P -> ?Q) => i1; go i2 i3 i4 i5 i6 ltac:(intro)
| |- (forall _, _) => intro; go i1 i2 i3 i4 i5 i6
end in
generalize ltac_mark; invert keep H; go i1 i2 i3 i4 i5 i6;
unfold eq' in *.
(** [inverts keep H] is same to [invert keep H] except that it applies [subst] to all the equalities generated by the inversion. *)
Tactic Notation "inverts" "keep" hyp(H) :=
inverts_tactic H ltac:(intro) ltac:(intro) ltac:(intro)
ltac:(intro) ltac:(intro) ltac:(intro).
(** [inverts keep H as X1 .. XN] is the same as [invert keep H as X1 .. XN] except that it applies [subst] to all the equalities generated by the inversion *)
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1) :=
inverts_tactic H ltac:(intros I1)
ltac:(intro) ltac:(intro) ltac:(intro) ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2)
ltac:(intro) ltac:(intro) ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intro) ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intros I4) ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intros I4) ltac:(intros I5) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) simple_intropattern(I6) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intros I4) ltac:(intros I5) ltac:(intros I6).
(** [inverts H] is same to [inverts keep H] except that it clears hypothesis [H]. *)
Tactic Notation "inverts" hyp(H) :=
inverts keep H; try clear H.
(** [inverts H as X1 .. XN] is the same as [inverts keep H as X1 .. XN] but it also clears the hypothesis [H]. *)
Tactic Notation "inverts_tactic" hyp(H) tactic(tac) :=
let H' := fresh "TEMP" in rename H into H'; tac H'; clear H'.
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1) :=
invert_tactic H (fun H => inverts keep H as I1).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
invert_tactic H (fun H => inverts keep H as I1 I2).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3 I4).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3 I4 I5).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) simple_intropattern(I6) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3 I4 I5 I6).
(** [inverts H as] performs an inversion on hypothesis [H], substitutes generated equalities, and put in the goal the other freshly-created hypotheses, for the user to name explicitly. [inverts keep H as] is the same except that it does not clear [H]. --Note: maybe reimplement [inverts] above using this one *)
Ltac inverts_as_tactic H :=
let rec go tt :=
match goal with
| |- (ltac_Mark -> _) => intros _
| |- (?x = ?y -> _) => let H := fresh "TEMP" in intro H;
first [ subst x | subst y ];
go tt
| |- (existT ?P ?p ?x = existT ?P ?p ?y -> _) =>
let H := fresh "TEMP" in intro H;
generalize (@inj_pair2 _ P p x y H);
clear H; go tt
| |- (forall _, _) =>
intro; let H := get_last_hyp tt in mark_to_generalize H; go tt
end in
pose ltac_mark; inversion H;
generalize ltac_mark; gen_until_mark;
go tt; gen_to_generalize; unfolds ltac_to_generalize;
unfold eq' in *.
Tactic Notation "inverts" "keep" hyp(H) "as" :=
inverts_as_tactic H.
Tactic Notation "inverts" hyp(H) "as" :=
inverts_as_tactic H; clear H.
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) simple_intropattern(I6) simple_intropattern(I7) :=
inverts H as; introv I1 I2 I3 I4 I5 I6 I7.
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) simple_intropattern(I6) simple_intropattern(I7)
simple_intropattern(I8) :=
inverts H as; introv I1 I2 I3 I4 I5 I6 I7 I8.
(** [lets_inverts E as I1 .. IN] is intuitively equivalent to [inverts E], with the difference that it applies to any expression and not just to the name of an hypothesis. *)
Ltac lets_inverts_base E cont :=
let H := fresh "TEMP" in lets H: E; try cont H.
Tactic Notation "lets_inverts" constr(E) :=
lets_inverts_base E ltac:(fun H => inverts H).
Tactic Notation "lets_inverts" constr(E) "as" simple_intropattern(I1) :=
lets_inverts_base E ltac:(fun H => inverts H as I1).
Tactic Notation "lets_inverts" constr(E) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
lets_inverts_base E ltac:(fun H => inverts H as I1 I2).
Tactic Notation "lets_inverts" constr(E) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
lets_inverts_base E ltac:(fun H => inverts H as I1 I2 I3).
Tactic Notation "lets_inverts" constr(E) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) :=
lets_inverts_base E ltac:(fun H => inverts H as I1 I2 I3 I4).
(* ================================================================= *)
(** ** Injection with Substitution *)
(** Underlying implementation of [injects] *)
Ltac injects_tactic H :=
let rec go _ :=
match goal with
| |- (ltac_Mark -> _) => intros _
| |- (?x = ?y -> _) => let H := fresh "TEMP" in intro H;
first [ subst x | subst y | idtac ];
go tt
end in
generalize ltac_mark; injection H; go tt.
(** [injects keep H] takes an hypothesis [H] of the form [C a1 .. aN = C b1 .. bN] and substitute all equalities [ai = bi] that have been generated. *)
Tactic Notation "injects" "keep" hyp(H) :=
injects_tactic H.
(** [injects H] is similar to [injects keep H] but clears the hypothesis [H]. *)
Tactic Notation "injects" hyp(H) :=
injects_tactic H; clear H.
(** [inject H as X1 .. XN] is the same as [injection] followed by [intros X1 .. XN] *)
Tactic Notation "inject" hyp(H) :=
injection H.
Tactic Notation "inject" hyp(H) "as" ident(X1) :=
injection H; intros X1.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) :=
injection H; intros X1 X2.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) ident(X3) :=
injection H; intros X1 X2 X3.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) ident(X3)
ident(X4) :=
injection H; intros X1 X2 X3 X4.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) ident(X3)
ident(X4) ident(X5) :=
injection H; intros X1 X2 X3 X4 X5.
(* ================================================================= *)
(** ** Inversion and Injection with Substitution --rough implementation *)
(** The tactics [inversions] and [injections] provided in this section are similar to [inverts] and [injects] except that they perform substitution on all equalities from the context and not only the ones freshly generated. The counterpart is that they have simpler implementations.  DEPRECATED: these tactics should no longer be used. *)
(** [inversions keep H] is the same as [inversions H] but it does not clear hypothesis [H]. *)
Tactic Notation "inversions" "keep" hyp(H) :=
inversion H; subst.
(** [inversions H] is a shortcut for [inversion H] followed by [subst] and [clear H]. It is a rough implementation of [inverts keep H] which behave badly when the proof context already contains equalities. It is provided in case the better implementation turns out to be too slow. *)
Tactic Notation "inversions" hyp(H) :=
inversion H; subst; try clear H.
(** [injections keep H] is the same as [injection H] followed by [intros] and [subst]. It is a rough implementation of [injects keep H] which behave badly when the proof context already contains equalities, or when the goal starts with a forall or an implication. *)
Tactic Notation "injections" "keep" hyp(H) :=
injection H; intros; subst.
(** [injections H] is the same as [injection H] followed by [clear H] and [intros] and [subst]. It is a rough implementation of [injects keep H] which behave badly when the proof context already contains equalities, or when the goal starts with a forall or an implication. *)
Tactic Notation "injections" "keep" hyp(H) :=
injection H; clear H; intros; subst.
(* ================================================================= *)
(** ** Case Analysis *)
(** [cases] is similar to [case_eq E] except that it generates the equality in the context and not in the goal, and generates the equality the other way round. The syntax [cases E as H] allows specifying the name [H] of that hypothesis. *)
Tactic Notation "cases" constr(E) "as" ident(H) :=
let X := fresh "TEMP" in
set (X := E) in *; def_to_eq_sym X H E;
destruct X.
Tactic Notation "cases" constr(E) :=
let H := fresh "Eq" in cases E as H.
(** [case_if_post H] is to be defined later as a tactic to clean up hypothesis [H] and the goal. By defaults, it looks for obvious contradictions. Currently, this tactic is extended in LibReflect to clean up boolean propositions. *)
Ltac case_if_post H :=
tryfalse.
(** [case_if] looks for a pattern of the form [if ?B then ?E1 else ?E2] in the goal, and perform a case analysis on [B] by calling [destruct B]. Subgoals containing a contradiction are discarded. [case_if] looks in the goal first, and otherwise in the first hypothesis that contains an [if] statement. [case_if in H] can be used to specify which hypothesis to consider. Syntaxes [case_if as Eq] and [case_if in H as Eq] allows to name the hypothesis coming from the case analysis. *)
Ltac case_if_on_tactic_core E Eq :=
match type of E with
| {_}+{_} => destruct E as [Eq | Eq]
| _ => let X := fresh "TEMP" in
sets_eq <- X Eq: E;
destruct X
end.
Ltac case_if_on_tactic E Eq :=
case_if_on_tactic_core E Eq; case_if_post Eq.
Tactic Notation "case_if_on" constr(E) "as" simple_intropattern(Eq) :=
case_if_on_tactic E Eq.
Tactic Notation "case_if" "as" simple_intropattern(Eq) :=
match goal with
| |- context [if ?B then _ else _] => case_if_on B as Eq
| K: context [if ?B then _ else _] |- _ => case_if_on B as Eq
end.
Tactic Notation "case_if" "in" hyp(H) "as" simple_intropattern(Eq) :=
match type of H with context [if ?B then _ else _] =>
case_if_on B as Eq end.
Tactic Notation "case_if" :=
let Eq := fresh "C" in case_if as Eq.
Tactic Notation "case_if" "in" hyp(H) :=
let Eq := fresh "C" in case_if in H as Eq.
(** [cases_if] is similar to [case_if] with two main differences: if it creates an equality of the form [x = y] and then substitutes it in the goal *)
Ltac cases_if_on_tactic_core E Eq :=
match type of E with
| {_}+{_} => destruct E as [Eq|Eq]; try subst_hyp Eq
| _ => let X := fresh "TEMP" in
sets_eq <- X Eq: E;
destruct X
end.
Ltac cases_if_on_tactic E Eq :=
cases_if_on_tactic_core E Eq; tryfalse; case_if_post Eq.
Tactic Notation "cases_if_on" constr(E) "as" simple_intropattern(Eq) :=
cases_if_on_tactic E Eq.
Tactic Notation "cases_if" "as" simple_intropattern(Eq) :=
match goal with
| |- context [if ?B then _ else _] => cases_if_on B as Eq
| K: context [if ?B then _ else _] |- _ => cases_if_on B as Eq
end.
Tactic Notation "cases_if" "in" hyp(H) "as" simple_intropattern(Eq) :=
match type of H with context [if ?B then _ else _] =>
cases_if_on B as Eq end.
Tactic Notation "cases_if" :=
let Eq := fresh "C" in cases_if as Eq.
Tactic Notation "cases_if" "in" hyp(H) :=
let Eq := fresh "C" in cases_if in H as Eq.
(** [case_ifs] is like [repeat case_if] *)
Ltac case_ifs_core :=
repeat case_if.
Tactic Notation "case_ifs" :=
case_ifs_core.
(** [destruct_if] looks for a pattern of the form [if ?B then ?E1 else ?E2] in the goal, and perform a case analysis on [B] by calling [destruct B]. It looks in the goal first, and otherwise in the first hypothesis that contains an [if] statement. *)
Ltac destruct_if_post := tryfalse.
Tactic Notation "destruct_if"
"as" simple_intropattern(Eq1) simple_intropattern(Eq2) :=
match goal with
| |- context [if ?B then _ else _] => destruct B as [Eq1|Eq2]
| K: context [if ?B then _ else _] |- _ => destruct B as [Eq1|Eq2]
end;
destruct_if_post.
Tactic Notation "destruct_if" "in" hyp(H)
"as" simple_intropattern(Eq1) simple_intropattern(Eq2) :=
match type of H with context [if ?B then _ else _] =>
destruct B as [Eq1|Eq2] end;
destruct_if_post.
Tactic Notation "destruct_if" "as" simple_intropattern(Eq) :=
destruct_if as Eq Eq.
Tactic Notation "destruct_if" "in" hyp(H) "as" simple_intropattern(Eq) :=
destruct_if in H as Eq Eq.
Tactic Notation "destruct_if" :=
let Eq := fresh "C" in destruct_if as Eq Eq.
Tactic Notation "destruct_if" "in" hyp(H) :=
let Eq := fresh "C" in destruct_if in H as Eq Eq.
(** [cases'] is provided for compatibility with [remember]. *)
(** [cases' E] is similar to [case_eq E] except that it generates the equality in the context and not in the goal. The syntax [cases' E as H] allows specifying the name [H] of that hypothesis. *)
Tactic Notation "cases'" constr(E) "as" ident(H) :=
let X := fresh "TEMP" in
set (X := E) in *; def_to_eq X H E;
destruct X.
Tactic Notation "cases'" constr(E) :=
let x := fresh "Eq" in cases' E as H.
(** [cases_if'] is similar to [cases_if] except that it generates the symmetric equality. *)
Ltac cases_if_on' E Eq :=
match type of E with
| {_}+{_} => destruct E as [Eq|Eq]; try subst_hyp Eq
| _ => let X := fresh "TEMP" in
sets_eq X Eq: E;
destruct X
end; case_if_post Eq.
Tactic Notation "cases_if'" "as" simple_intropattern(Eq) :=
match goal with
| |- context [if ?B then _ else _] => cases_if_on' B Eq
| K: context [if ?B then _ else _] |- _ => cases_if_on' B Eq
end.
Tactic Notation "cases_if'" :=
let Eq := fresh "C" in cases_if' as Eq.
(* ################################################################# *)
(** * Induction *)
(** [inductions E] is a shorthand for [dependent induction E]. [inductions E gen X1 .. XN] is a shorthand for [dependent induction E generalizing X1 .. XN]. *)
Ltac inductions_post :=
unfold eq' in *.
Tactic Notation "inductions" ident(E) :=
dependent induction E; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) :=
dependent induction E generalizing X1; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2) :=
dependent induction E generalizing X1 X2; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2)
ident(X3) :=
dependent induction E generalizing X1 X2 X3; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2)
ident(X3) ident(X4) :=
dependent induction E generalizing X1 X2 X3 X4; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2)
ident(X3) ident(X4) ident(X5) :=
dependent induction E generalizing X1 X2 X3 X4 X5; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2)
ident(X3) ident(X4) ident(X5) ident(X6) :=
dependent induction E generalizing X1 X2 X3 X4 X5 X6; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2)
ident(X3) ident(X4) ident(X5) ident(X6) ident(X7) :=
dependent induction E generalizing X1 X2 X3 X4 X5 X6 X7; inductions_post.
Tactic Notation "inductions" ident(E) "gen" ident(X1) ident(X2)
ident(X3) ident(X4) ident(X5) ident(X6) ident(X7) ident(X8) :=
dependent induction E generalizing X1 X2 X3 X4 X5 X6 X7 X8; inductions_post.
(** [induction_wf IH: E X] is used to apply the well-founded induction principle, for a given well-founded relation. It applies to a goal [PX] where [PX] is a proposition on [X]. First, it sets up the goal in the form [(fun a => P a) X], using [pattern X], and then it applies the well-founded induction principle instantiated on [E].  Here [E] may be either: - a proof of [wf R] for [R] of type [A->A->Prop] - a binary relation of type [A->A->Prop] - a measure of type [A -> nat] // only when LibWf is used. *)
(* DEPRECATED Tactic Notation "induction_wf" ident(IH) ":" constr(E) ident(X) := pattern X; apply (well_founded_ind E); clear X; intros X IH. *)
Ltac induction_wf_process_wf_hyp tt := (* refined in LibWf *)
idtac.
Ltac induction_wf_process_measure E := (* refined in LibWf *)
fail.
Ltac induction_wf_core_then E X cont :=
let clearX tt :=
first [ clear X | fail 3 "the variable on which the induction is done appears in the hypotheses" ] in
pattern X;
first [ eapply (@well_founded_ind _ E)
| eapply (@well_founded_ind _ (E _))
| eapply (@well_founded_ind _ (E _ _))
| eapply (@well_founded_ind _ (E _ _ _))
| induction_wf_process_measure E
| applys well_founded_ind E ];
clearX tt;
first [ induction_wf_process_wf_hyp tt
| intros X; cont tt ].
Ltac induction_wf_core IH E X :=
induction_wf_core_then E X ltac:(fun _ => intros IH).
Tactic Notation "induction_wf" ident(IH) ":" constr(E) ident(X) :=
induction_wf_core IH E X.
(** Induction on the height of a derivation: the helper tactic [induct_height] helps proving the equivalence of the auxiliary judgment that includes a counter for the maximal height (see LibTacticsDemos for an example) *)

Lemma induct_height_max2 : forall n1 n2 : nat,
exists n, n1 < n /\ n2 < n.