(* FIXME; instead of importing inf, import only the right metalib module *)
(* Tactics for the project *)
(* TODO - automated f_equal (etc) - split forall ands - pick fresh and specialize all atom -> P hyps - the organization (in different tactics, etc) is probably perfectible *)
(* Dynamic type, useful for some tactics *)
Inductive Dyn : Type := dyn : forall {T : Type}, T -> Dyn.
Ltac unwrap_dyn d :=
match d with
| dyn ?v => v
end.
(* TODO: This is subsumed by pcess_hyps down there. Make sure we never need to use *specifically* this tactic, then remove it *)
Ltac split_hyp :=
repeat (
match goal with
| [ H : _ /\ _ |- _ ] => destruct H
end).
Ltac rewrite_and_clear eq :=
first [rewrite -> eq | rewrite <- eq]; clear eq.
Ltac try_rewrite_and_clear eq :=
first [rewrite -> ! eq | rewrite <- ! eq | idtac]; clear eq.
(* Only forward (i.e., ->) *)
Ltac try_rewrite_and_clear_f eq :=
first [rewrite ! eq | idtac]; clear eq.
(* Integrate these in the big match below? *)
(* Find an generalized equality, rewrite it, and clear it *)
(* TODO: need much more parameters *)
Ltac find_eq_rew_clear :=
match goal with
| [ eq : forall t1,                _ = _ |- _ ] => rewrite_and_clear eq
| [ eq : forall t1 t2,             _ = _ |- _ ] => rewrite_and_clear eq
| [ eq : forall t1 t2 t3,          _ = _ |- _ ] => rewrite_and_clear eq
| [ eq : forall t1 t2 t3 t4,       _ = _ |- _ ] => rewrite_and_clear eq
| [ eq : forall t1 t2 t3 t4 t5,    _ = _ |- _ ] => rewrite_and_clear eq
| [ eq : forall t1 t2 t3 t4 t5 t6, _ = _ |- _ ] => rewrite_and_clear eq
end.
(* Tactic equivalent to subst, but which also tries to rewrite universally quantified equalities *)
(* FIXME: good name *)
Ltac subst_forall :=
repeat find_eq_rew_clear.
(**** Solvers ****)
(* The <solve> versions are strict: they solve the goal or do nothing. The <nosolve> versions do whatever they can *)
Tactic Notation "basic_nosolve_n"     int_or_var(n) :=
intuition (subst; eauto n).
Tactic Notation "basic_nosolve_n'"    int_or_var(n) :=
intuition (subst; simpl in *; subst; eauto n; try done).
Tactic Notation "basic_nosolve_fo_n"  int_or_var(n) :=
firstorder (subst; eauto n).
Tactic Notation "basic_nosolve_fo_n'" int_or_var(n) :=
firstorder (subst_forall; simpl in *; subst_forall; eauto n; try done).
Tactic Notation "basic_solve_n"     int_or_var(n) := try solve [basic_nosolve_n     n].
Tactic Notation "basic_solve_n'"    int_or_var(n) := try solve [basic_nosolve_n'    n].
Tactic Notation "basic_solve_fo_n"  int_or_var(n) := try solve [basic_nosolve_fo_n  n].
Tactic Notation "basic_solve_fo_n'" int_or_var(n) := try solve [basic_nosolve_fo_n' n].
(* `eauto` and the like default to `5` *)
Ltac basic_nosolve     := basic_nosolve_n     5.
Ltac basic_nosolve'    := basic_nosolve_n'    5.
Ltac basic_nosolve_fo  := basic_nosolve_fo_n  5.
Ltac basic_nosolve_fo' := basic_nosolve_fo_n' 5.
Ltac basic_solve     := try solve [basic_nosolve].
Ltac basic_solve'    := try solve [basic_nosolve'].
Ltac basic_solve_fo  := try solve [basic_nosolve_fo].
Ltac basic_solve_fo' := try solve [basic_nosolve_fo'].
Ltac solve_by_inv_hyp_about A :=
multimatch goal with
| [ H : context [?A] |- _ ] => solve [inversion H; basic_solve]
end.
Ltac revert_all :=
repeat match goal with
| [ H : _ |- _ ] => revert H
end.
Ltac revert_all_with t :=
repeat match goal with
| [ H : _ |- _ ] => try t H; revert dependent H
end.
(* TODO: use match instead to respect names *)
Ltac intro_all_with t :=
repeat
(let x := fresh in intro x; try (t x)).
Ltac disjunction_assumption :=
match goal with
| [H : ?P |- ?P]     => exact H
| [H : ?P |- ?P ∨ _] => left; exact H
| [       |- _  ∨ _] => right; disjunction_assumption
end.
(* This version does NOT check if the inversion produces only one goal *)
Ltac invert_and_clear H := inversion H; clear H.
(* Technical, tactic-internal *)
Definition wrap : forall P : Prop, P -> P * True := fun _ p => (p, I).
Ltac wrap_hyp H := apply wrap in H.

(* Specifications: *)
(* FIXME: copying this here, temporarily *)
Lemma AnnCtx_uniq G : AnnCtx G -> uniq G.