Require Export RelDefinitions.
Require Export RelOperators.
Require Export Relators.
(** ** The [monotonicity] tactic *)
(** The purpose of the [monotonicity] tactic is to automatically select and apply a theorem of the form [Monotonic ?m ?R] in order to make progress when the goal is an applied relation. Compared with setoid rewriting, [monotonicity] is less powerful, but more direct and simple. This means it is easier to debug, and it can seamlessly handle dependent types and heterogenous relations.  The [monotonicity] tactic applies to a goal of the form [R (f x1 x2 ... xn) (g y1 y2 ... yn)]. The basic idea is to first locate a relational property declared by the user relating [f] and [g], namely an instance of [Related f g R'], then to locate and instance of [RElim] which will allow us to use [R' f g] to make progress on the current goal. Most of the time, [f] and [g] will be identical so that the corresponding relational property can be writted [Monotonic f R'].  For instance, we can register the monotonicity of [plus] as an instance of [Monotonic plus (lt ++> lt ++> lt)]. Given an goal of the form [plus 5 6 < plus x 5], the monotonicity tactic will use that property and, using the [RElim] rule for [++>], construct an instance of [RElim (lt ++> lt ++> lt) plus plus (lt 5 7 -> lt x 5 -> lt (plus 5 6) (plus x 5))] to apply on the goal, leaving us with the subgoals [5 < 7] and [x < 5].  While this basic principle is rather straightforward, there are some subtleties that we have to consider in order to build a reliable system: the relational property we need to use may be about a prefix [f x1 x2 ... xk] ([k] < [n]) of the application that still has some arguments applied; we want to be able to exploit [subrel] relationships to extend the applicability of the declared relational properties; we need to be need to be careful about how we treat existential variables. *)
(** *** Truncating applications *)
(** The first complication arises from the fact that [f] and [g] may themselves be partial applications, so the goal may in fact be of the form [R (f a1 a2 ... ak x1 x2 ... xn) (g b1 b2 ... bl y1 y2 ... yn)], with [k] ≠ [l], where the relational property to apply will be of the form [Related R' (f a1 a2 ... ak) (g b1 b2 ... bl)]. This is particularly relevant when [f] and [g] may have implicit typeclass arguments, or type arguments that cannot be covered by our dependent relators without causing universe inconsistency issues.  Finding the head terms [f] and [g], then locating a property [Related R' f g] is rather straightforward, however in this case we must only drop the last [n] arguments from the applications before we look up the [Related] instance, with no way of guessing the correct value of [n]. We want to avoid having to peel off arguments one by one and performing a [Related] search at each step, so we ask the user to pitch in and declare instances of [Params f n] and [Params g n] to guide our search when partial applications with heads [f] and [g] are involved.  Note that the term in the goal itself may only be partially applied. Suppose [f x1 x2 ... xn] is in expecting [p] more arguments, and that we find an instance of [Params f q]; then we should only drop [q - p] arguments to obtain the prefix [f x1 x2 ... x_(n+p-q)].  The class [QueryParams m1 m2 n] asserts that the head terms of [m1] and [m2] have [Params] declarations that instruct us to remove [n] arguments from each of them before looking up a corresponding relational property. *)
Class QueryParams {A B} (m1: A) (m2: B) (n: nat).
(** If the head term on either side is an existential variable, we only check [Params] declarations for the other side. This auxiliary lemma is used to trigger this step; [p] should be the number of unapplied arguments in whichever term [m1] of [m2] we extracted the non-existential head term [h] from. *)
(** If neither head term is an existential variable, then the [Params] declarations for both should agree. *)
(** Now we need to choose which one of these lemmas to apply in a given situation, and compute the appropriate values for [p]. We compute [p] by essentially counting the products in the type of the related terms [m]. Note that we need to make sure we "open up" types such as [subrel] to expose the products, accomplished here by using [eval cbv]. *)
(** FIXME: [count_unapplied_params] fails if [m] has a dependent type, due to [t'] being a term under binders rather than a closed term. This means [monotonicity] can't handle goal where the related terms have dependent types if the corresponding relational property is about a partial application. *)
Ltac count_unapplied_params m :=
let rec count t :=
lazymatch t with
| forall x, ?t' => let n := count t' in constr:(S n)
| _ => constr:(O)
end in
let t := type of m in
let t := eval cbv in t in
count t.
Ltac query_params m1 m2 :=
let rec head t := lazymatch t with ?t' _ => head t' | _ => t end in
let h1 := head m1 in
let p1 := count_unapplied_params m1 in
let h2 := head m2 in
let p2 := count_unapplied_params m2 in
first
[ not_evar h1; not_evar h2; eapply (query_params_both h1 h2 p1 p2)
| not_evar h1; is_evar  h2; eapply (query_params_one h1 p1)
| is_evar  h1; not_evar h2; eapply (query_params_one h2 p2) ].
Hint Extern 10 (QueryParams ?m1 ?m2 _) =>
query_params m1 m2 : typeclass_instances.
(** Now that we can figure out how many parameters to drop, we use that information to construct the pair of prefixes we use to locate the relational property. We try the following (in this order): - the whole applications [f x1 ... xn] and [g y1 ... yn]; - prefixes constructed as above from user-declared [Params] instances; - the smallest prefix we can obtain by dropping arguments from both applications simulataneously.  Again, we need to take into account that either application could contain an eixstential variable. When we encounter an existential variable while chopping off arguments, we short-circuit the process and simply generate a new evar to serve as the shortened version. *)
Ltac pass_evar_to k :=
let Av := fresh "A" in evar (Av : Type);
let A := eval red in Av in clear Av;
let av := fresh "a" in evar (av : A);
let a := eval red in av in clear av;
k a.
(** The class [RemoveParams] implements the concurrent removal of [n] parameters from applications [m1] and [m2]. *)
Class RemoveParams {A B C D} (n: nat) (m1: A) (m2: B) (f: C) (g: D).
Ltac remove_params_from n m k :=
lazymatch n with
| O => k m
| S ?n' =>
lazymatch m with
| ?m' _ => remove_params_from n' m' k
| _ => is_evar m; pass_evar_to k
end
end.
Ltac remove_params n m1 m2 f g :=
remove_params_from n m1 ltac:(fun m1' => unify f m1';
remove_params_from n m2 ltac:(fun m2' => unify g m2')).
Hint Extern 1 (RemoveParams ?n ?m1 ?m2 ?f ?g) =>
remove_params n m1 m2 f g; constructor : typeclass_instances.
(** The class [RemoveAllParams] implements the concurrent removal of as many arguments as possible. *)
Class RemoveAllParams {A B C D} (m1: A) (m2: B) (f: C) (g: D).
Ltac remove_all_params_from m k :=
lazymatch m with
| ?m' _ => remove_all_params_from m' k
| _ => not_evar m; k m
end.
Ltac remove_all_params m1 m2 f g :=
first
[ is_evar m2;
remove_all_params_from m1
ltac:(fun m1' => unify f m1'; pass_evar_to ltac:(fun m2' => unify g m2'))
| is_evar m1;
remove_all_params_from m2
ltac:(fun m2' => unify g m2'; pass_evar_to ltac:(fun m1' => unify f m1'))
| lazymatch m1 with ?m1' _ =>
lazymatch m2 with ?m2' _ =>
remove_all_params m1' m2' f g
end
end
| not_evar m1; unify f m1;
not_evar m2; unify g m2 ].
Hint Extern 1 (RemoveAllParams ?m1 ?m2 ?f ?g) =>
remove_all_params m1 m2 f g; constructor : typeclass_instances.
(** *** Selecting a relational property *)
(** With this machinery in place, we can implement the search for candidate relational properties to use in conjunction with a given goal [Q]. *)
Class CandidateProperty {A B} (R': rel A B) f g (Q: Prop) :=
candidate_related: R' f g.
Instance as_is_candidate {A B} (R R': rel A B) m1 m2:
Related m1 m2 R' ->
CandidateProperty R' m1 m2 (R m1 m2) | 10.
Instance remove_params_candidate {A B C D} R (m1: A) (m2: B) R' (f: C) (g: D) n:
QueryParams m1 m2 n ->
RemoveParams n m1 m2 f g ->
Related f g R' ->
CandidateProperty R' f g (R m1 m2) | 20.
Instance remove_all_params_candidate {A B C D} R (m1:A) (m2:B) R' (f:C) (g:D):
RemoveAllParams m1 m2 f g ->
Related f g R' ->
CandidateProperty R' f g (R m1 m2) | 30.
(** We also attempt to use any matching hypothesis. This takes priority and bypasses any [Params] declaration. We assume that we will always want such hypotheses to be used, at least when the left- or right-hand side matches exactly. There is a possibility that this ends up being too broad for some applications, for which we'll want to restrict ourselves to [Related] instances explicitely defined by the user. If this turns out to be the case, we can introduce an intermediate class with more parameters to control the sources of the relational properties we use, and perhaps have some kind of normalization process akin to what is done in [Coq.Classes.Morphisms].  We don't hesitate to instantiate evars in the goal or hypothesis; if the types or skeletons are compatible this is likely what we want. In one practical case where this is needed, my hypothesis is something like [R (f ?x1 ?y1 42) (f ?x2 ?y2 24)], and I want [?x1], [?x2], [?y1], [?y2] to be matched against the goal. In case the use of [unify] below is too broad, we could develop a strategy whereby at least one of the head terms [f], [g] should match exactly.  We also consider the possibility that the relation's arguments may be flipped in the hypothesis. In that case we wrap the relation with [flip] to construct a corresponding [CandidateProperty]. This [flip] will most likely be handled at the [RElim] stage by [flip_relim]. The hypothesis relation itself may be of the form [flip R]. In that case, the [CandidateProperty] will use [flip (flip R)] and [flip_relim] will simply be used twice. In more common and subtle variation, the hypothesis relation is not directly of this form, but should either be instantiated or refined (through [subrel]) into such a form. This is handled by [rimpl_flip_subrel].  Such issues are especially common when solving a goal of the form [(R --> R') f g], or goals generated by the setoid rewriting system of the form [Proper (?R1 ==> ... ==> ?Rn ==> flip impl)], and where the [?Ri]s will generally need to be instantiated as [flip ?Ri']. We do not pursue a similar strategy when looking up candidates from the typeclass instance database, because we expect the user to normalize such properties before they declare them, for example declare an instance of [Related (R1 ++> R2 --> R3) g f] rather than an instance of [Related (R1 --> R2 ++> flip R3) f g].  Note that it is important that we reduce the goal to [?R ?m ?n] before we use [eexact]: if the relation in the hypothesis is an existential variable, we don't want it unified against [CandidateProperty _ _ _ (_ ?m ?n)]. *)

Lemma flip_context_candidate {A B} (R: rel A B) x y : R y x -> flip R x y.