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. *)

(* Specifications: *)
Lemma query_params_one {A B C} (h: A) p (m1: B) (m2: C) n:
Params h (p + n) ->
QueryParams m1 m2 n.