(** * The [RDestruct] class *)

(** ** Tactics *)

(** The [rdestruct] tactic locates and uses an [RDestruct] instance
  for a given hypothesis. *)

Ltac rdestruct H :=
  lazymatch type of H with
    | ?R ?m ?n =>
      not_evar R;
      pattern m, n;
      apply (rdestruct (R:=R) m n H);
      clear H;
      Delay.split_conjunction
  end.(** The [rinversion] tactic is similar, but it remembers the terms
  related by the destructed hypothesis. *)

Ltac rinversion_tac H Hm Hn :=
  lazymatch type of H with
    | ?R ?m ?n =>
      not_evar R;
      pattern m, n;
      lazymatch goal with
        | |- ?Q _ _ =>
          generalize (eq_refl m), (eq_refl n);
          change ((fun x y => Delay.delayed_goal (x = m -> y = n -> Q x y)) m n);
          apply (rdestruct (R:=R) m n H);
          Delay.split_conjunction;
          intros Hm Hn
      end
  end.(** ** Introducing the hypothesis to be destructed *)

(** The goal of [rdestruct] is usually to make progress on a goal
  relating two [match] constructions, by breaking down the terms being
  matched. The instances below will attempt to prove automatically
  that these terms are related, then destruct the resulting hypothesis
  in order to break them down and reduce the [match]es.

  If proving the destructed hypothesis automatically is not possible,
  the user can use the following tactic to introduce it manually. *)

Ltac rdestruct_assert :=
  lazymatch goal with
    | |- _ (match ?m with _ => _ end) (match ?n with _ => _ end) =>
      let Tm := type of m in
      let Tn := type of n in
      let R := fresh "R" in
      evar (R: rel Tm Tn);
      assert (R m n); subst R
  end.(** ** [RDestruct] steps *)

(** Sometimes we want to remember what the destructed terms were.
  To make this possible, we use the following relation to wrap the
  subgoals generated by the [RDestruct] instance. *)

Definition rdestruct_result {A B} m n (Q: rel A B): rel A B :=
  fun x y => m = x /\ n = y -> Q x y.

Lemma rdestruct_rstep {A B} m n (R: rel A B) P (Q: rel _ _): RAuto (R m n) -> RDestruct R P -> P (rdestruct_result m n Q) -> Q m n.