(**
 It is essential to interlock the production of the Mem constructor inside
 the branch of the predType match, to ensure that unifying mem A with
 Mem [eta ?p] sets ?p := toP A (or ?p := P if toP = id and A = [eta P]),
 rather than topred pT A, had we put mem A := Mem (topred A).
**)
Definition mem T (pT : predType T) : pT -> mem_pred T :=
  let: PredType toP := pT in fun A => Mem [eta toP A].Notation "x \in A" := (in_mem x (mem A)) (only printing) : bool_scope.Notation "x \notin A" := (~~ (x \in A)) : bool_scope.Notation "A =i B" := (eq_mem (mem A) (mem B)) : type_scope.Notation "{ 'subset' A <= B }" := (sub_mem (mem A) (mem B)) : type_scope.Notation "[ 'in' A ]" := (in_mem^~ (mem A))
  (at level 0, format "[ 'in'  A ]") : function_scope.Notation "[ 'mem' A ]" :=
  (pred_of_simpl (simpl_of_mem (mem A))) (only parsing) : function_scope.Notation "[ 'predI' A & B ]" := (predI [in A] [in B]) : function_scope.Notation "[ 'predU' A & B ]" := (predU [in A] [in B]) : function_scope.Notation "[ 'predD' A & B ]" := (predD [in A] [in B]) : function_scope.Notation "[ 'predC' A ]" := (predC [in A]) : function_scope.Notation "[ 'preim' f 'of' A ]" := (preim f [in A]) : function_scope.Notation "[ 'pred' x 'in' A ]" := [pred x | x \in A] : function_scope.Notation "[ 'pred' x 'in' A | E ]" := [pred x | x \in A & E] : function_scope.Notation "[ 'pred' x 'in' A | E1 & E2 ]" :=
  [pred x | x \in A & E1 && E2 ] : function_scope.Notation "[ 'rel' x y 'in' A & B | E ]" :=
  [rel x y | (x \in A) && (y \in B) && E] : function_scope.Notation "[ 'rel' x y 'in' A & B ]" :=
  [rel x y | (x \in A) && (y \in B)] : function_scope.Notation "[ 'rel' x y 'in' A | E ]" := [rel x y in A & A | E] : function_scope.Notation "[ 'rel' x y 'in' A ]" := [rel x y in A & A] : function_scope.(** Aliases of pred T that let us tag instances of simpl_pred as applicative
  or collective, via bespoke coercions. This tagging will give control over
  the simplification behaviour of inE and other rewriting lemmas below.
    For this control to work it is crucial that collective_of_simpl _not_
  be convertible to either applicative_of_simpl or pred_of_simpl. Indeed
  they differ here by a commutative conversion (of the match and lambda).
 **)
Definition applicative_pred T := pred T.Definition collective_pred T := pred T.Coercion applicative_pred_of_simpl T (sp : simpl_pred T) : applicative_pred T :=
  fun_of_simpl sp.Coercion collective_pred_of_simpl T (sp : simpl_pred T) : collective_pred T :=
  let: SimplFun p := sp in p.Implicit Types (p : pred T) (pT : predType T) (sp : simpl_pred T).Implicit Types (mp : mem_pred T).(**
 The following four bespoke structures provide fine-grained control over
 matching the various predicate forms. While all four follow a common pattern
 of using a canonical projection to match a particular form of predicate
 (in pred T, simpl_pred, mem_pred and mem_pred, respectively), and display
 the matched predicate in the structure type, each is in fact used for a
 different, specific purpose:
  - registered_applicative_pred: this user-facing structure is used to
    declare values of type pred T meant to be used applicatively. The
    structure parameter merely displays this same value, and is used to avoid
    undesirable, visible occurrence of the structure in the right hand side
    of rewrite rules such as app_predE.
      There is a canonical instance of registered_applicative_pred for values
    of the applicative_of_simpl coercion, which handles the
       Definition Apred : applicative_pred T := [pred x | ...] idiom.
    This instance is mainly intended for the in_applicative component of inE,
    in conjunction with manifest_mem_pred and applicative_mem_pred.
  - manifest_simpl_pred: the only instance of this structure matches manifest
    simpl_pred values of the form SimplPred p, displaying p in the structure
    type. This structure is used in in_simpl to detect and selectively expand
    collective predicates of this form. An explicit SimplPred p pattern would
    _NOT_ work for this purpose, as then the left-hand side of in_simpl would
    reduce to in_mem ?x (Mem [eta ?p]) and would thus match _any_ instance
    of \in, not just those arising from a manifest simpl_pred.
  - manifest_mem_pred: similar to manifest_simpl_pred, the one instance of this
    structure matches manifest mem_pred values of the form Mem [eta ?p]. The
    purpose is different however: to match and display in ?p the actual
    predicate appearing in an ... \in ... expression matched by the left hand
    side of the in_applicative component of inE; then
  - applicative_mem_pred is a telescope refinement of manifest_mem_pred p with
    a default constructor that checks that the predicate p is the value of a
    registered_applicative_pred; any unfolding occurring during this check
    does _not_ affect the value of p passed to in_applicative, since that
    has been fixed earlier by the manifest_mem_pred match. In particular the
    definition of a predicate using the applicative_pred_of_simpl idiom above
    will not be expanded - this very case is the reason in_applicative uses
    a mem_pred telescope in its left hand side. The more straightforward
    ?x \in applicative_pred_value ?ap (equivalent to in_mem ?x (Mem ?ap))
    with ?ap : registered_applicative_pred ?p would set ?p := [pred x | ...]
    rather than ?p := Apred in the example above.
 Also note that the in_applicative component of inE must be come before the
 in_simpl one, as the latter also matches terms of the form x \in Apred.
 Finally, no component of inE matches x \in Acoll, when
   Definition Acoll : collective_pred T := [pred x | ...].
 as the collective_pred_of_simpl is _not_ convertible to pred_of_simpl.  **)

Structure registered_applicative_pred p := RegisteredApplicativePred {
  applicative_pred_value :> pred T;
  _ : applicative_pred_value = p
}.(**
 The following four bespoke structures provide fine-grained control over
 matching the various predicate forms. While all four follow a common pattern
 of using a canonical projection to match a particular form of predicate
 (in pred T, simpl_pred, mem_pred and mem_pred, respectively), and display
 the matched predicate in the structure type, each is in fact used for a
 different, specific purpose:
  - registered_applicative_pred: this user-facing structure is used to
    declare values of type pred T meant to be used applicatively. The
    structure parameter merely displays this same value, and is used to avoid
    undesirable, visible occurrence of the structure in the right hand side
    of rewrite rules such as app_predE.
      There is a canonical instance of registered_applicative_pred for values
    of the applicative_of_simpl coercion, which handles the
       Definition Apred : applicative_pred T := [pred x | ...] idiom.
    This instance is mainly intended for the in_applicative component of inE,
    in conjunction with manifest_mem_pred and applicative_mem_pred.
  - manifest_simpl_pred: the only instance of this structure matches manifest
    simpl_pred values of the form SimplPred p, displaying p in the structure
    type. This structure is used in in_simpl to detect and selectively expand
    collective predicates of this form. An explicit SimplPred p pattern would
    _NOT_ work for this purpose, as then the left-hand side of in_simpl would
    reduce to in_mem ?x (Mem [eta ?p]) and would thus match _any_ instance
    of \in, not just those arising from a manifest simpl_pred.
  - manifest_mem_pred: similar to manifest_simpl_pred, the one instance of this
    structure matches manifest mem_pred values of the form Mem [eta ?p]. The
    purpose is different however: to match and display in ?p the actual
    predicate appearing in an ... \in ... expression matched by the left hand
    side of the in_applicative component of inE; then
  - applicative_mem_pred is a telescope refinement of manifest_mem_pred p with
    a default constructor that checks that the predicate p is the value of a
    registered_applicative_pred; any unfolding occurring during this check
    does _not_ affect the value of p passed to in_applicative, since that
    has been fixed earlier by the manifest_mem_pred match. In particular the
    definition of a predicate using the applicative_pred_of_simpl idiom above
    will not be expanded - this very case is the reason in_applicative uses
    a mem_pred telescope in its left hand side. The more straightforward
    ?x \in applicative_pred_value ?ap (equivalent to in_mem ?x (Mem ?ap))
    with ?ap : registered_applicative_pred ?p would set ?p := [pred x | ...]
    rather than ?p := Apred in the example above.
 Also note that the in_applicative component of inE must be come before the
 in_simpl one, as the latter also matches terms of the form x \in Apred.
 Finally, no component of inE matches x \in Acoll, when
   Definition Acoll : collective_pred T := [pred x | ...].
 as the collective_pred_of_simpl is _not_ convertible to pred_of_simpl.  **)

Structure registered_applicative_pred p := RegisteredApplicativePred {
  applicative_pred_value :> pred T;
  _ : applicative_pred_value = p
}.(**
 The following four bespoke structures provide fine-grained control over
 matching the various predicate forms. While all four follow a common pattern
 of using a canonical projection to match a particular form of predicate
 (in pred T, simpl_pred, mem_pred and mem_pred, respectively), and display
 the matched predicate in the structure type, each is in fact used for a
 different, specific purpose:
  - registered_applicative_pred: this user-facing structure is used to
    declare values of type pred T meant to be used applicatively. The
    structure parameter merely displays this same value, and is used to avoid
    undesirable, visible occurrence of the structure in the right hand side
    of rewrite rules such as app_predE.
      There is a canonical instance of registered_applicative_pred for values
    of the applicative_of_simpl coercion, which handles the
       Definition Apred : applicative_pred T := [pred x | ...] idiom.
    This instance is mainly intended for the in_applicative component of inE,
    in conjunction with manifest_mem_pred and applicative_mem_pred.
  - manifest_simpl_pred: the only instance of this structure matches manifest
    simpl_pred values of the form SimplPred p, displaying p in the structure
    type. This structure is used in in_simpl to detect and selectively expand
    collective predicates of this form. An explicit SimplPred p pattern would
    _NOT_ work for this purpose, as then the left-hand side of in_simpl would
    reduce to in_mem ?x (Mem [eta ?p]) and would thus match _any_ instance
    of \in, not just those arising from a manifest simpl_pred.
  - manifest_mem_pred: similar to manifest_simpl_pred, the one instance of this
    structure matches manifest mem_pred values of the form Mem [eta ?p]. The
    purpose is different however: to match and display in ?p the actual
    predicate appearing in an ... \in ... expression matched by the left hand
    side of the in_applicative component of inE; then
  - applicative_mem_pred is a telescope refinement of manifest_mem_pred p with
    a default constructor that checks that the predicate p is the value of a
    registered_applicative_pred; any unfolding occurring during this check
    does _not_ affect the value of p passed to in_applicative, since that
    has been fixed earlier by the manifest_mem_pred match. In particular the
    definition of a predicate using the applicative_pred_of_simpl idiom above
    will not be expanded - this very case is the reason in_applicative uses
    a mem_pred telescope in its left hand side. The more straightforward
    ?x \in applicative_pred_value ?ap (equivalent to in_mem ?x (Mem ?ap))
    with ?ap : registered_applicative_pred ?p would set ?p := [pred x | ...]
    rather than ?p := Apred in the example above.
 Also note that the in_applicative component of inE must be come before the
 in_simpl one, as the latter also matches terms of the form x \in Apred.
 Finally, no component of inE matches x \in Acoll, when
   Definition Acoll : collective_pred T := [pred x | ...].
 as the collective_pred_of_simpl is _not_ convertible to pred_of_simpl.  **)

Structure registered_applicative_pred p := RegisteredApplicativePred {
  applicative_pred_value :> pred T;
  _ : applicative_pred_value = p
}.Definition ApplicativePred p := RegisteredApplicativePred (erefl p).Canonical applicative_pred_applicative sp :=
  ApplicativePred (applicative_pred_of_simpl sp).Structure manifest_simpl_pred p := ManifestSimplPred {
  simpl_pred_value :> simpl_pred T;
  _ : simpl_pred_value = SimplPred p
}.Structure manifest_simpl_pred p := ManifestSimplPred {
  simpl_pred_value :> simpl_pred T;
  _ : simpl_pred_value = SimplPred p
}.Structure manifest_simpl_pred p := ManifestSimplPred {
  simpl_pred_value :> simpl_pred T;
  _ : simpl_pred_value = SimplPred p
}.Canonical expose_simpl_pred p := ManifestSimplPred (erefl (SimplPred p)).Structure manifest_mem_pred p := ManifestMemPred {
  mem_pred_value :> mem_pred T;
  _ : mem_pred_value = Mem [eta p]
}.Structure manifest_mem_pred p := ManifestMemPred {
  mem_pred_value :> mem_pred T;
  _ : mem_pred_value = Mem [eta p]
}.Structure manifest_mem_pred p := ManifestMemPred {
  mem_pred_value :> mem_pred T;
  _ : mem_pred_value = Mem [eta p]
}.Canonical expose_mem_pred p := ManifestMemPred (erefl (Mem [eta p])).Structure applicative_mem_pred p :=
  ApplicativeMemPred {applicative_mem_pred_value :> manifest_mem_pred p}.Structure applicative_mem_pred p :=
  ApplicativeMemPred {applicative_mem_pred_value :> manifest_mem_pred p}.Structure applicative_mem_pred p :=
  ApplicativeMemPred {applicative_mem_pred_value :> manifest_mem_pred p}.Canonical check_applicative_mem_pred p (ap : registered_applicative_pred p) :=
  [eta @ApplicativeMemPred ap].

(* Specifications: *)
Lemma mem_topred pT (pp : pT) : mem (topred pp) = mem pp.
Lemma topredE pT x (pp : pT) : topred pp x = (x \in pp).
Lemma app_predE x p (ap : registered_applicative_pred p) : ap x = (x \in p).
Lemma in_applicative x p (amp : applicative_mem_pred p) : in_mem x amp = p x.
Lemma in_collective x p (msp : manifest_simpl_pred p) : (x \in collective_pred_of_simpl msp) = p x.
Lemma in_simpl x p (msp : manifest_simpl_pred p) : in_mem x (Mem [eta pred_of_simpl msp]) = p x.
Lemma unfold_in x p : (x \in ([eta p] : pred T)) = p x.
Lemma simpl_predE p : SimplPred p =1 p.