Require Export RelDefinitions.
(** * 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.
Tactic Notation "rinversion" constr(H) "as" ident(Hl) "," ident(Hr) :=
rinversion_tac H Hl Hr.
Tactic Notation "rinversion" hyp(H) :=
let Hl := fresh H "l" in
let Hr := fresh H "r" in
rinversion_tac H Hl Hr.
Tactic Notation "rinversion" constr(H) :=
let Hl := fresh "Hl" in
let Hr := fresh "Hr" in
rinversion_tac H Hl Hr.
(** ** 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.
(** To use [RDestruct] instances to reduce a goal involving pattern matching [G] := [_ (match m with _ end) (match n with _ end)], we need to establish that [m] and [n] are related by some relation [R], then locate an instance of [RDestruct] that corresponds to [R]. It is essential that this happens in one step. At some point, I tried to split the process in two different [RStep]s, so that the user could control the resolution of the [?R m n] subgoal. However, in situation where [RDestruct] is not the right strategy, this may push a [delayed rauto] into a dead end. Thankfully, now we can use the [RAuto] typeclass to solve the [?R m n] subgoal in one swoop. *)

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.