(******************************************************************************)
(* Predicates, i.e., packaged functions to bool.                              *)
(* - pred T, the basic type for predicates over a type T, is simply an alias  *)
(* for T -> bool.                                                             *)
(* We actually distinguish two kinds of predicates, which we call applicative *)
(* and collective, based on the syntax used to test them at some x in T:      *)
(* - For an applicative predicate P, one uses prefix syntax:                  *)
(*     P x                                                                    *)
(*   Also, most operations on applicative predicates use prefix syntax as     *)
(*   well (e.g., predI P Q).                                                  *)
(* - For a collective predicate A, one uses infix syntax:                     *)
(*     x \in A                                                                *)
(*   and all operations on collective predicates use infix syntax as well     *)
(*   (e.g., [predI A & B]).                                                   *)
(* There are only two kinds of applicative predicates:                        *)
(* - pred T, the alias for T -> bool mentioned above                          *)
(* - simpl_pred T, an alias for simpl_fun T bool with a coercion to pred T    *)
(*   that auto-simplifies on application (see ssrfun).                        *)
(* On the other hand, the set of collective predicate types is open-ended via *)
(* - predType T, a Structure that can be used to put Canonical collective     *)
(*   predicate interpretation on other types, such as lists, tuples,          *)
(*   finite sets, etc.                                                        *)
(* Indeed, we define such interpretations for applicative predicate types,    *)
(* which can therefore also be used with the infix syntax, e.g.,              *)
(*     x \in predI P Q                                                        *)
(* Moreover these infix forms are convertible to their prefix counterpart     *)
(* (e.g., predI P Q x which in turn simplifies to P x && Q x). The converse   *)
(* is not true, however; collective predicate types cannot, in general, be    *)
(* general, be used applicatively, because of the "uniform inheritance"       *)
(* restriction on implicit coercions.                                         *)
(*   However, we do define an explicit generic coercion                       *)
(* - mem : forall (pT : predType), pT -> mem_pred T                           *)
(*   where mem_pred T is a variant of simpl_pred T that preserves the infix   *)
(*   syntax, i.e., mem A x auto-simplifies to x \in A.                        *)
(* Indeed, the infix "collective" operators are notation for a prefix         *)
(* operator with arguments of type mem_pred T or pred T, applied to coerced   *)
(* collective predicates, e.g.,                                               *)
(*      Notation "x \in A" := (in_mem x (mem A)).                             *)
(* This prevents the variability in the predicate type from interfering with  *)
(* the application of generic lemmas. Moreover this also makes it much easier *)
(* to define generic lemmas, because the simplest type -- pred T -- can be    *)
(* used as the type of generic collective predicates, provided one takes care *)
(* not to use it applicatively; this avoids the burden of having to declare a *)
(* different predicate type for each predicate parameter of each section or   *)
(* lemma.                                                                     *)
(*   This trick is made possible by the fact that the constructor of the      *)
(* mem_pred T type aligns the unification process, forcing a generic          *)
(* "collective" predicate A : pred T to unify with the actual collective B,   *)
(* which mem has coerced to pred T via an internal, hidden implicit coercion, *)
(* supplied by the predType structure for B. Users should take care not to    *)
(* inadvertently "strip" (mem B) down to the coerced B, since this will       *)
(* expose the internal coercion: Coq will display a term B x that cannot be   *)
(* typed as such. The topredE lemma can be used to restore the x \in B        *)
(* syntax in this case. While -topredE can conversely be used to change       *)
(* x \in P into P x, it is safer to use the inE and memE lemmas instead, as   *)
(* they do not run the risk of exposing internal coercions. As a consequence  *)
(* it is better to explicitly cast a generic applicative pred T to simpl_pred *)
(* using the SimplPred constructor, when it is used as a collective predicate *)
(* (see, e.g., Lemma eq_big in bigop).                                        *)
(*   We also sometimes "instantiate" the predType structure by defining a     *)
(* coercion to the sort of the predPredType structure. This works better for  *)
(* types such as {set T} that have subtypes that coerce to them, since the    *)
(* same coercion will be inserted by the application of mem. It also lets us  *)
(* turn any Type aT : predArgType into the total predicate over that type,    *)
(* i.e., fun _: aT => true. This allows us to write, e.g., #|'I_n| for the    *)
(* cardinal of the (finite) type of integers less than n.                     *)
(*   Collective predicates have a specific extensional equality,              *)
(*   - A =i B,                                                                *)
(* while applicative predicates use the extensional equality of functions,    *)
(*   - P =1 Q                                                                 *)
(* The two forms are convertible, however.                                    *)
(* We lift boolean operations to predicates, defining:                        *)
(* - predU (union), predI (intersection), predC (complement),                 *)
(*   predD (difference), and preim (preimage, i.e., composition)              *)
(* For each operation we define three forms, typically:                       *)
(* - predU : pred T -> pred T -> simpl_pred T                                 *)
(* - [predU A & B], a Notation for predU (mem A) (mem B)                      *)
(* - xpredU, a Notation for the lambda-expression inside predU,               *)
(*     which is mostly useful as an argument of =1, since it exposes the head *)
(*     head constant of the expression to the ssreflect matching algorithm.   *)
(* The syntax for the preimage of a collective predicate A is                 *)
(* - [preim f of A]                                                           *)
(* Finally, the generic syntax for defining a simpl_pred T is                 *)
(* - [pred x : T | P(x)], [pred x | P(x)], [pred x in A | P(x)], etc.         *)
(* We also support boolean relations, but only the applicative form, with     *)
(* types                                                                      *)
(* - rel T, an alias for T -> pred T                                          *)
(* - simpl_rel T, an auto-simplifying version, and syntax                     *)
(*   [rel x y | P(x,y)], [rel x y in A & B | P(x,y)], etc.                    *)
(* The notation [rel of fA] can be used to coerce a function returning a      *)
(* collective predicate to one returning pred T.                              *)
(*   Finally, note that there is specific support for ambivalent predicates   *)
(* that can work in either style, as per this file's head descriptor.         *)
(******************************************************************************)

Definition pred T := T -> bool.Identity Coercion fun_of_pred : pred >-> Funclass.Definition rel T := T -> pred T.Identity Coercion fun_of_rel : rel >-> Funclass.Notation xpred0 := (fun _ => false).Notation xpredT := (fun _ => true).Notation xpredI := (fun (p1 p2 : pred _) x => p1 x && p2 x).Notation xpredU := (fun (p1 p2 : pred _) x => p1 x || p2 x).Notation xpredC := (fun (p : pred _) x => ~~ p x).Notation xpredD := (fun (p1 p2 : pred _) x => ~~ p2 x && p1 x).Notation xpreim := (fun f (p : pred _) x => p (f x)).Notation xrelU := (fun (r1 r2 : rel _) x y => r1 x y || r2 x y).Definition subpred (p1 p2 : pred T) := forall x, p1 x -> p2 x.Definition subrel (r1 r2 : rel T) := forall x y, r1 x y -> r2 x y.Definition simpl_pred := simpl_fun T bool.Definition applicative_pred := pred T.Definition collective_pred := pred T.Definition SimplPred (p : pred T) : simpl_pred := SimplFun p.Coercion pred_of_simpl (p : simpl_pred) : pred T := fun_of_simpl p.Coercion applicative_pred_of_simpl (p : simpl_pred) : applicative_pred :=
  fun_of_simpl p.(* Note: applicative_of_simpl is convertible to pred_of_simpl, while *)
(* collective_of_simpl is not. *)

Definition pred0 := SimplPred xpred0.Definition predT := SimplPred xpredT.Definition predI p1 p2 := SimplPred (xpredI p1 p2).Definition predU p1 p2 := SimplPred (xpredU p1 p2).Definition predC p := SimplPred (xpredC p).Definition predD p1 p2 := SimplPred (xpredD p1 p2).Definition preim rT f (d : pred rT) := SimplPred (xpreim f d).Definition simpl_rel := simpl_fun T (pred T).Coercion rel_of_simpl_rel (r : simpl_rel) : rel T := fun x y => r x y.Definition relU r1 r2 := SimplRel (xrelU r1 r2).

(* Specifications: *)
Lemma subrelUl r1 r2 : subrel r1 (relU r1 r2).
Lemma subrelUr r1 r2 : subrel r2 (relU r1 r2).