(* 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.(* 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.(* `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)).(* 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: *)
Lemma AnnCtx_uniq G : AnnCtx G -> uniq G.