Library probsa.util.boolp


From mathcomp Require Import all_ssreflect.


Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.

Declare Scope box_scope.
Declare Scope quant_scope.


Axiom functional_extensionality_dep :
        (A : Type) (B : A Type) (f g : x : A, B x),
       ( x : A, f x = g x) f = g.
Axiom propositional_extensionality :
        P Q : Prop, P Q P = Q.

Axiom constructive_indefinite_description :
   (A : Type) (P : A Prop),
  ( x : A, P x) {x : A | P x}.
Notation cid := constructive_indefinite_description.

Lemma cid2 (A : Type) (P Q : A Prop) :
  (exists2 x : A, P x & Q x) {x : A | P x & Q x}.
Proof.
movePQA; suff: {x | P x Q x} by move⇒ [a [*]]; a.
by apply: cid; case: PQAx; x.
Qed.

Record mextentionality := {
  _ : (P Q : Prop), (P Q) (P = Q);
  _ : {T U : Type} (f g : T U),
        ( x, f x = g x) f = g;
}.

Fact extentionality : mextentionality.
Proof.
split.
- exact: propositional_extensionality.
- by moveT U f g; apply: functional_extensionality_dep.
Qed.

Lemma propext (P Q : Prop) : (P Q) (P = Q).
Proof. by have [propext _] := extentionality; apply: propext. Qed.

Lemma funext {T U : Type} (f g : T U) : (f =1 g) f = g.
Proof. by case: extentionality_; apply. Qed.

Lemma propeqE (P Q : Prop) : (P = Q) = (P Q).
Proof. by apply: propext; split⇒ [->|/propext]. Qed.

Lemma propeqP (P Q : Prop) : (P = Q) (P Q).
Proof. by rewrite propeqE. Qed.

Lemma funeqE {T U : Type} (f g : T U) : (f = g) = (f =1 g).
Proof. by rewrite propeqE; split⇒ [->//|/funext]. Qed.

Lemma funeq2E {T U V : Type} (f g : T U V) : (f = g) = (f =2 g).
Proof.
by rewrite propeqE; split⇒ [->//|?]; rewrite funeqEx; rewrite funeqE.
Qed.

Lemma funeq3E {T U V W : Type} (f g : T U V W) :
  (f = g) = ( x y z, f x y z = g x y z).
Proof.
by rewrite propeqE; split⇒ [->//|?]; rewrite funeq2Ex y; rewrite funeqE.
Qed.

Lemma funeqP {T U : Type} (f g : T U) : (f = g) (f =1 g).
Proof. by rewrite funeqE. Qed.

Lemma funeq2P {T U V : Type} (f g : T U V) : (f = g) (f =2 g).
Proof. by rewrite funeq2E. Qed.

Lemma funeq3P {T U V W : Type} (f g : T U V W) :
  (f = g) ( x y z, f x y z = g x y z).
Proof. by rewrite funeq3E. Qed.

Lemma predeqE {T} (P Q : T Prop) : (P = Q) = ( x, P x Q x).
Proof.
by rewrite propeqE; split⇒ [->//|?]; rewrite funeqEx; rewrite propeqE.
Qed.

Lemma predeq2E {T U} (P Q : T U Prop) :
   (P = Q) = ( x y, P x y Q x y).
Proof.
by rewrite propeqE; split⇒ [->//|?]; rewrite funeq2E⇒ ??; rewrite propeqE.
Qed.

Lemma predeq3E {T U V} (P Q : T U V Prop) :
   (P = Q) = ( x y z, P x y z Q x y z).
Proof.
by rewrite propeqE; split⇒ [->//|?]; rewrite funeq3E⇒ ???; rewrite propeqE.
Qed.

Lemma predeqP {T} (A B : T Prop) : (A = B) ( x, A x B x).
Proof. by rewrite predeqE. Qed.

Lemma predeq2P {T U} (P Q : T U Prop) :
   (P = Q) ( x y, P x y Q x y).
Proof. by rewrite predeq2E. Qed.

Lemma predeq3P {T U V} (P Q : T U V Prop) :
   (P = Q) ( x y z, P x y z Q x y z).
Proof. by rewrite predeq3E. Qed.

Lemma propT {P : Prop} : P P = True.
Proof. by movep; rewrite propeqE. Qed.

Lemma Prop_irrelevance (P : Prop) (x y : P) : x = y.
Proof. by move: x (x) y ⇒ /propT→ [] []. Qed.
#[global] Hint Resolve Prop_irrelevance : core.

Record mclassic := {
  _ : (P : Prop), {P} + {¬P};
  _ : T, Choice.mixin_of T
}.

Lemma choice X Y (P : X Y Prop) :
  ( x, y, P x y) {f & x, P x (f x)}.
Proof. by move⇒ /(_ _)/constructive_indefinite_description -/all_tag. Qed.

Theorem EM P : P ¬ P.
Proof.
pose U val := fun Q : boolQ = val P.
have Uex val : b, U val b by val; left.
pose f val := projT1 (cid (Uex val)).
pose Uf val : U val (f val) := projT2 (cid (Uex val)).
have : f true != f false P.
  have [] := (Uf true, Uf false); rewrite /U.
  by move⇒ [->|?] [->|?] ; do ?[by right]; left.
move⇒ [/eqP fTFN|]; [rightp|by left]; apply: fTFN.
have UTF : U true = U false by rewrite predeqE /Ub; split_; right.
rewrite /f; move: (Uex true) (Uex false); rewrite UTFp1 p2.
by congr (projT1 (cid _)).
Qed.

Lemma pselect (P : Prop): {P} + {¬P}.
Proof.
have : b, if b then P else ¬ P.
  by case: (EM P); [ true| false].
by move⇒ /cid [[]]; [left|right].
Qed.

Lemma pselectT T : (T False) + T.
Proof.
have [/cid[]//|NT] := pselect ( t : T, True); first by right.
by leftt; case: NT; t.
Qed.

Lemma classic : mclassic.
Proof.
split⇒ [|T]; first exact: pselect.
(fun (P : pred T) (n : nat) ⇒
  if pselect ( x, P x) isn't left ex then None
  else Some (projT1 (cid ex)))
  ⇒ [P n x|P [x Px]|P Q /funext → //].
  by case: pselect ⇒ // ex [<- ]; case: cid.
by 0; case: pselect ⇒ // -[]; x.
Qed.

Lemma gen_choiceMixin {T : Type} : Choice.mixin_of T.
Proof. by case: classic. Qed.

Lemma pdegen (P : Prop): P = True P = False.
Proof. by have [p|Np] := pselect P; [left|right]; rewrite propeqE. Qed.

Lemma lem (P : Prop): P ¬P.
Proof. by case: (pselect P); tauto. Qed.

Lemma trueE : true = True :> Prop.
Proof. by rewrite propeqE; split. Qed.

Lemma falseE : false = False :> Prop.
Proof. by rewrite propeqE; split. Qed.

Lemma propF (P : Prop) : ¬ P P = False.
Proof. by movep; rewrite propeqE; tauto. Qed.

Lemma eq_fun T rT (U V : T rT) :
  ( x : T, U x = V x) (fun xU x) = (fun xV x).
Proof. by move⇒ /funext→. Qed.

Lemma eq_fun2 T1 T2 rT (U V : T1 T2 rT) :
  ( x y, U x y = V x y) (fun x yU x y) = (fun x yV x y).
Proof. by moveUV; rewrite funeq2Ex y; rewrite UV. Qed.

Lemma eq_fun3 T1 T2 T3 rT (U V : T1 T2 T3 rT) :
  ( x y z, U x y z = V x y z)
  (fun x y zU x y z) = (fun x y zV x y z).
Proof. by moveUV; rewrite funeq3Ex y z; rewrite UV. Qed.

Lemma eq_forall T (U V : T Prop) :
  ( x : T, U x = V x) ( x, U x) = ( x, V x).
Proof. by movee; rewrite propeqE; split⇒ ??; rewrite (e,=^~e). Qed.

Lemma eq_forall2 T S (U V : x : T, S x Prop) :
  ( x y, U x y = V x y) ( x y, U x y) = ( x y, V x y).
Proof. by moveUV; apply/eq_forallx; apply/eq_forall. Qed.

Lemma eq_forall3 T S R (U V : (x : T) (y : S x), R x y Prop) :
  ( x y z, U x y z = V x y z)
  ( x y z, U x y z) = ( x y z, V x y z).
Proof. by moveUV; apply/eq_forall2x y; apply/eq_forall. Qed.

Lemma eq_exists T (U V : T Prop) :
  ( x : T, U x = V x) ( x, U x) = ( x, V x).
Proof.
by movee; rewrite propeqE; split⇒ - [] x ?; x; rewrite (e,=^~e).
Qed.

Lemma eq_exists2 T S (U V : x : T, S x Prop) :
  ( x y, U x y = V x y) ( x y, U x y) = ( x y, V x y).
Proof. by moveUV; apply/eq_existsx; apply/eq_exists. Qed.

Lemma eq_exists3 T S R (U V : (x : T) (y : S x), R x y Prop) :
  ( x y z, U x y z = V x y z)
  ( x y z, U x y z) = ( x y z, V x y z).
Proof. by moveUV; apply/eq_exists2x y; apply/eq_exists. Qed.

Lemma eq_exist T (P : T Prop) (s t : T) (p : P s) (q : P t) :
  s = t exist P s p = exist P t q.
Proof. by movest; case: _ / st in q *; apply/congr1. Qed.

Lemma forall_swap T S (U : (x : T) (y : S), Prop) :
   ( x y, U x y) = ( y x, U x y).
Proof. by rewrite propeqE; split. Qed.

Lemma exists_swap T S (U : (x : T) (y : S), Prop) :
   ( x y, U x y) = ( y x, U x y).
Proof. by rewrite propeqE; split ⇒ -[x [y]]; y, x. Qed.

Lemma reflect_eq (P : Prop) (b : bool) : reflect P b P = b.
Proof. by rewrite propeqE; exact: rwP. Qed.

Definition asbool (P : Prop) :=
  if pselect P then true else false.

Notation "`[< P >]" := (asbool P) : bool_scope.

Lemma asboolE (P : Prop) : `[<P>] = P :> Prop.
Proof. by rewrite propeqE /asbool; case: pselect; split. Qed.

Lemma asboolP (P : Prop) : reflect P `[<P>].
Proof. by apply: (equivP idP); rewrite asboolE. Qed.

Lemma asboolb (b : bool) : `[< b >] = b.
Proof. by apply/asboolP/idP. Qed.

Lemma asboolPn (P : Prop) : reflect (¬ P) (~~ `[<P>]).
Proof. by rewrite /asbool; case: pselecth; constructor. Qed.

Lemma asboolW (P : Prop) : `[<P>] P.
Proof. by case: asboolP. Qed.

Lemma asboolT (P : Prop) : P `[<P>].
Proof. by case: asboolP. Qed.

Lemma asboolF (P : Prop) : ¬ P `[<P>] = false.
Proof. by apply/introF/asboolP. Qed.

Lemma eq_opE (T : eqType) (x y : T) : (x == y : Prop) = (x = y).
Proof. by apply/propext; split⇒ /eqP. Qed.

Lemma is_true_inj : injective is_true.
Proof. by move⇒ [] []; rewrite ?(trueE, falseE) ?propeqE; tauto. Qed.

Definition gen_eq (T : Type) (u v : T) := `[<u = v>].
Lemma gen_eqP (T : Type) : Equality.axiom (@gen_eq T).
Proof. by movex y; apply: (iffP (asboolP _)). Qed.
Definition gen_eqMixin {T : Type} := EqMixin (@gen_eqP T).

Canonical arrow_eqType (T : Type) (T' : eqType) :=
  EqType (T T') gen_eqMixin.
Canonical arrow_choiceType (T : Type) (T' : choiceType) :=
  ChoiceType (T T') gen_choiceMixin.

Definition dep_arrow_eqType (T : Type) (T' : T eqType) :=
  EqType ( x : T, T' x) gen_eqMixin.
Definition dep_arrow_choiceClass (T : Type) (T' : T choiceType) :=
  Choice.Class (Equality.class (dep_arrow_eqType T')) gen_choiceMixin.
Definition dep_arrow_choiceType (T : Type) (T' : T choiceType) :=
  Choice.Pack (dep_arrow_choiceClass T').

Canonical Prop_eqType := EqType Prop gen_eqMixin.
Canonical Prop_choiceType := ChoiceType Prop gen_choiceMixin.

Section classicType.
Variable T : Type.
Definition classicType := T.
Canonical classicType_eqType := EqType classicType gen_eqMixin.
Canonical classicType_choiceType := ChoiceType classicType gen_choiceMixin.
End classicType.
Notation "'{classic' T }" := (classicType T)
 (format "'{classic' T }") : type_scope.

Section eclassicType.
Variable T : eqType.
Definition eclassicType : Type := T.
Canonical eclassicType_eqType := EqType eclassicType (Equality.class T).
Canonical eclassicType_choiceType := ChoiceType eclassicType gen_choiceMixin.
End eclassicType.
Notation "'{eclassic' T }" := (eclassicType T)
 (format "'{eclassic' T }") : type_scope.

Definition canonical_of T U (sort : U T) := (G : T Type),
  ( x', G (sort x')) x, G x.
Notation canonical_ sort := (@canonical_of _ _ sort).
Notation canonical T E := (@canonical_of T E id).

Lemma canon T U (sort : U T) : ( x, y, sort y = x) canonical_ sort.
Proof. by move⇒ + G Gs x ⇒ /(_ x)/cid[x' <-]. Qed.
Arguments canon {T U sort} x.

Lemma Peq : canonical Type eqType.
Proof. by apply: canonT; [eqType of {classic T}]. Qed.
Lemma Pchoice : canonical Type choiceType.
Proof. by apply: canonT; [choiceType of {classic T}]. Qed.
Lemma eqPchoice : canonical eqType choiceType.
Proof. by apply: canonT; [choiceType of {eclassic T}]; case: T. Qed.

Lemma not_True : (¬ True) = False. Proof. exact/propext. Qed.
Lemma not_False : (¬ False) = True. Proof. by apply/propext; split_. Qed.

Lemma asbool_equiv_eq {P Q : Prop} : (P Q) `[<P>] = `[<Q>].
Proof. by rewrite -propeqE ⇒ →. Qed.

Lemma asbool_equiv_eqP {P Q : Prop} b : reflect Q b (P Q) `[<P>] = b.
Proof. by moveQ_b [PQ QP]; apply/asboolP/Q_b. Qed.

Lemma asbool_equiv {P Q : Prop} : (P Q) (`[<P>] `[<Q>]).
Proof. by move/asbool_equiv_eq→. Qed.

Lemma asbool_eq_equiv {P Q : Prop} : `[<P>] = `[<Q>] (P Q).
Proof. by moveeq; split⇒ /asboolP; rewrite (eq, =^~ eq) ⇒ /asboolP. Qed.

Lemma and_asboolP (P Q : Prop) : reflect (P Q) (`[< P >] && `[< Q >]).
Proof.
apply: (iffP idP); first by case/andP ⇒ /asboolP p /asboolP q.
by case⇒ /asboolP→ /asboolP→.
Qed.

Lemma and3_asboolP (P Q R : Prop) :
  reflect [/\ P, Q & R] [&& `[< P >], `[< Q >] & `[< R >]].
Proof.
apply: (iffP idP); first by case/and3P ⇒ /asboolP p /asboolP q /asboolP r.
by case ⇒ /asboolP → /asboolP → /asboolP →.
Qed.

Lemma or_asboolP (P Q : Prop) : reflect (P Q) (`[< P >] || `[< Q >]).
Proof.
apply: (iffP idP); first by case/orP⇒ /asboolP; [left | right].
by case⇒ /asboolP→ //=; rewrite orbT.
Qed.

Lemma or3_asboolP (P Q R : Prop) :
  reflect [\/ P, Q | R] [|| `[< P >], `[< Q >] | `[< R >]].
Proof.
apply: (iffP idP); last by case⇒ [| |] /asboolP → //=; rewrite !orbT.
by case/orP⇒ [/asboolP p|/orP[]/asboolP]; [exact:Or31|exact:Or32|exact:Or33].
Qed.

Lemma asbool_neg {P : Prop} : `[<¬ P>] = ~~ `[<P>].
Proof. by apply/idP/asboolPn⇒ [/asboolP|/asboolT]. Qed.

Lemma asbool_or {P Q : Prop} : `[<P Q>] = `[<P>] || `[<Q>].
Proof. exact: (asbool_equiv_eqP (or_asboolP _ _)). Qed.

Lemma asbool_and {P Q : Prop} : `[<P Q>] = `[<P>] && `[<Q>].
Proof. exact: (asbool_equiv_eqP (and_asboolP _ _)). Qed.

Lemma imply_asboolP {P Q : Prop} : reflect (P Q) (`[<P>] ==> `[<Q>]).
Proof.
apply: (iffP implyP)=> [PQb /asboolP/PQb/asboolW //|].
by movePQ /asboolP/PQ/asboolT.
Qed.

Lemma asbool_imply {P Q : Prop} : `[<P Q>] = `[<P>] ==> `[<Q>].
Proof. exact: (asbool_equiv_eqP imply_asboolP). Qed.

Lemma imply_asboolPn (P Q : Prop) : reflect (P ¬ Q) (~~ `[<P Q>]).
Proof.
apply: (iffP idP).
by rewrite asbool_imply negb_imply -asbool_neg ⇒ /and_asboolP.
by move/and_asboolP; rewrite asbool_neg -negb_imply asbool_imply.
Qed.

Lemma forall_asboolP {T : Type} (P : T Prop) :
  reflect ( x, `[<P x>]) (`[< x, P x>]).
Proof.
apply: (iffP idP); first by move/asboolPPx x; apply/asboolP.
by movePx; apply/asboolPx; apply/asboolP.
Qed.

Lemma exists_asboolP {T : Type} (P : T Prop) :
  reflect ( x, `[<P x>]) (`[< x, P x>]).
Proof.
apply: (iffP idP); first by case/asboolPx Px; x; apply/asboolP.
by casex bPx; apply/asboolP; x; apply/asboolP.
Qed.


Lemma notT (P : Prop) : P = False ¬ P. Proof. by move→. Qed.

Lemma contrapT P : ¬ ¬ P P.
Proof.
by move/asboolPnnnb; apply/asboolP; apply: contraR nnb ⇒ /asboolPn /asboolP.
Qed.

Lemma notTE (P : Prop) : (¬ P) P = False.
Proof. by case: (pdegen P)=> →. Qed.

Lemma notFE (P : Prop) : (¬ P) = False P.
Proof. move/notT; exact: contrapT. Qed.

Lemma notK : involutive not.
Proof.
moveP; case: (pdegen P)=> ->; last by apply: notTE; intuition.
by rewrite [¬ True]notTE //; case: (pdegen (¬ False)) ⇒ // /notFE.
Qed.

Lemma contra_notP (Q P : Prop) : (¬ Q P) ¬ P Q.
Proof.
movecb /asboolPn nb; apply/asboolP.
by apply: contraR nb ⇒ /asboolP /cb /asboolP.
Qed.

Lemma contraPP (Q P : Prop) : (¬ Q ¬ P) P Q.
Proof.
movecb /asboolP hb; apply/asboolP.
by apply: contraLR hb ⇒ /asboolP /cb /asboolPn.
Qed.

Lemma contra_notT b (P : Prop) : (~~ b P) ¬ P b.
Proof. by movebP; apply: contra_notP ⇒ /negP. Qed.

Lemma contraPT (P : Prop) b : (~~ b ¬ P) P b.
Proof. by move⇒ /contra_notT; rewrite notK. Qed.

Lemma contraTP b (Q : Prop) : (¬ Q ~~ b) b Q.
Proof. by moveQB; apply: contraPP ⇒ /QB/negP. Qed.

Lemma contraNP (P : Prop) (b : bool) : (¬ P b) ~~ b P.
Proof. by move⇒ /contra_notP + /negP ⇒ /[apply]. Qed.

Lemma contra_neqP (T : eqType) (x y : T) P : (¬ P x = y) x != y P.
Proof. by movePxy; apply: contraNP ⇒ /Pxy/eqP. Qed.

Lemma contra_eqP (T : eqType) (x y : T) (Q : Prop) : (¬ Q x != y) x = y Q.
Proof. by moveQxy /eqP; apply: contraTP. Qed.

Lemma wlog_neg P : (¬ P P) P.
Proof. by move⇒ ?; case: (pselect P). Qed.

Lemma not_inj : injective not. Proof. exact: can_inj notK. Qed.
Lemma notLR P Q : (P = ¬ Q) (¬ P) = Q. Proof. exact: canLR notK. Qed.

Lemma notRL P Q : (¬ P) = Q P = ¬ Q. Proof. exact: canRL notK. Qed.

Lemma iff_notr (P Q : Prop) : (P ¬ Q) (¬ P Q).
Proof. by split⇒ [/propext ->|/propext <-]; rewrite notK. Qed.

Lemma iff_not2 (P Q : Prop) : (¬ P ¬ Q) (P Q).
Proof. by split⇒ [/iff_notr|PQ]; [|apply/iff_notr]; rewrite notK. Qed.


Definition predp T := T Prop.

Identity Coercion fun_of_pred : predp >-> Funclass.

Definition relp T := T predp T.

Identity Coercion fun_of_rel : rel >-> Funclass.

Notation xpredp0 := (fun _False).
Notation xpredpT := (fun _True).
Notation xpredpI := (fun (p1 p2 : predp _) xp1 x p2 x).
Notation xpredpU := (fun (p1 p2 : predp _) xp1 x p2 x).
Notation xpredpC := (fun (p : predp _) x¬ p x).
Notation xpredpD := (fun (p1 p2 : predp _) x¬ p2 x p1 x).
Notation xpreimp := (fun f (p : predp _) xp (f x)).
Notation xrelpU := (fun (r1 r2 : relp _) x yr1 x y r2 x y).

Definition pred0p (T : Type) (P : predp T) : bool := `[<P =1 xpredp0>].
Prenex Implicits pred0p.

Lemma pred0pP (T : Type) (P : predp T) : reflect (P =1 xpredp0) (pred0p P).
Proof. by apply: (iffP (asboolP _)). Qed.

Lemma forallp_asboolPn {T} {P : T Prop} :
  reflect ( x : T, ¬ P x) (~~ `[< x : T, P x>]).
Proof.
apply: (iffP idP)=> [/asboolPn NP x Px|NP].
by apply/NP; x. by apply/asboolP⇒ -[x]; apply/NP.
Qed.

Lemma existsp_asboolPn {T} {P : T Prop} :
  reflect ( x : T, ¬ P x) (~~ `[< x : T, P x>]).
Proof.
apply: (iffP idP); last by casex NPx; apply/asboolPn⇒ /(_ x).
move/asboolPnNP; apply/asboolP/negbNE/asboolPnh.
by apply/NPx; apply/asboolP/negbNE/asboolPnNPx; apply/h; x.
Qed.

Lemma asbool_forallNb {T : Type} (P : pred T) :
  `[< x : T, ~~ (P x) >] = ~~ `[< x : T, P x >].
Proof.
apply: (asbool_equiv_eqP forallp_asboolPn);
  by splith x; apply/negP/h.
Qed.

Lemma asbool_existsNb {T : Type} (P : pred T) :
  `[< x : T, ~~ (P x) >] = ~~ `[< x : T, P x >].
Proof.
apply: (asbool_equiv_eqP existsp_asboolPn);
  by split⇒ -[x h]; x; apply/negP.
Qed.

Lemma not_implyP (P Q : Prop) : ¬ (P Q) P ¬ Q.
Proof.
split⇒ [/asboolP|[p nq pq]]; [|exact/nq/pq].
by rewrite asbool_neg ⇒ /imply_asboolPn.
Qed.

Lemma not_andP (P Q : Prop) : ¬ (P Q) ¬ P ¬ Q.
Proof.
split ⇒ [/asboolPn|[|]]; try by apply: contra_not ⇒ -[].
by rewrite asbool_and negb_and ⇒ /orP[]/asboolPn; [left|right].
Qed.

Lemma not_and3P (P Q R : Prop) : ¬ [/\ P, Q & R] [\/ ¬ P, ¬ Q | ¬ R].
Proof.
split⇒ [/and3_asboolP|/or3_asboolP].
by rewrite 2!negb_and -3!asbool_neg ⇒ /or3_asboolP.
by rewrite 3!asbool_neg -2!negb_and ⇒ /and3_asboolP.
Qed.

Lemma not_orP (P Q : Prop) : ¬ (P Q) ¬ P ¬ Q.
Proof.
split; [apply: contra_notP ⇒ /not_andP|apply: contraPnotAB; apply/not_andP];
  by rewrite 2!notK.
Qed.

Lemma not_implyE (P Q : Prop) : (¬ (P Q)) = (P ¬ Q).
Proof. by rewrite propeqE not_implyP. Qed.

Lemma orC (P Q : Prop) : (P Q) = (Q P).
Proof. by rewrite propeqE; split⇒ [[]|[]]; [right|left|right|left]. Qed.

Lemma orA : associative or.
Proof. by moveP Q R; rewrite propeqE; split⇒ [|]; tauto. Qed.

Lemma andC (P Q : Prop) : (P Q) = (Q P).
Proof. by rewrite propeqE; split⇒ [[]|[]]. Qed.

Lemma andA : associative and.
Proof. by moveP Q R; rewrite propeqE; split⇒ [|]; tauto. Qed.

Lemma forallNE {T} (P : T Prop) : ( x, ¬ P x) = ¬ x, P x.
Proof.
by rewrite propeqE; split ⇒ [fP [x /fP]//|nexP x Px]; apply: nexP; x.
Qed.

Lemma existsNE {T} (P : T Prop) : ( x, ¬ P x) = ¬ x, P x.
Proof.
rewrite propeqE; split⇒ [[x Px] aP //|NaP].
by apply: contrapT; rewrite -forallNEaP; apply: NaPx; apply: contrapT.
Qed.

Lemma existsNP T (P : T Prop) : ( x, ¬ P x) ¬ x, P x.
Proof. by rewrite existsNE. Qed.

Lemma not_existsP T (P : T Prop) : ( x, P x) ¬ x, ¬ P x.
Proof. by rewrite forallNE notK. Qed.

Lemma forallNP T (P : T Prop) : ( x, ¬ P x) ¬ x, P x.
Proof. by rewrite forallNE. Qed.

Lemma not_forallP T (P : T Prop) : ( x, P x) ¬ x, ¬ P x.
Proof. by rewrite existsNE notK. Qed.

Lemma exists2P T (P Q : T Prop) :
  (exists2 x, P x & Q x) x, P x Q x.
Proof. by split⇒ [[x ? ?] | [x []]]; x. Qed.

Lemma not_exists2P T (P Q : T Prop) :
  (exists2 x, P x & Q x) ¬ x, ¬ P x ¬ Q x.
Proof.
rewrite exists2P not_existsP.
by split; apply: contra_notPQx x; apply/not_andP; apply: PQx.
Qed.

Lemma forall2NP T (P Q : T Prop) :
  ( x, ¬ P x ¬ Q x) ¬ (exists2 x, P x & Q x).
Proof.
split⇒ [PQ [t Pt Qt]|PQ t]; first by have [] := PQ t.
by rewrite -not_andP ⇒ -[Pt Qt]; apply PQ; t.
Qed.

Lemma forallPNP T (P Q : T Prop) :
  ( x, P x ¬ Q x) ¬ (exists2 x, P x & Q x).
Proof.
split⇒ [PQ [t Pt Qt]|PQ t]; first by have [] := PQ t.
by movePt Qt; apply: PQ; t.
Qed.

Lemma existsPNP T (P Q : T Prop) :
  (exists2 x, P x & ¬ Q x) ¬ ( x, P x Q x).
Proof.
split⇒ [[x Px NQx] /(_ x Px)//|]; apply: contra_notP ⇒ + x Px.
by apply: contra_notPNQx; x.
Qed.

Module FunOrder.
Section FunOrder.
Import Order.TTheory.
Variables (aT : Type) (d : unit) (T : porderType d).
Implicit Types f g h : aT T.

Lemma fun_display : unit. Proof. exact: tt. Qed.

Definition lef f g := `[< x, (f x g x)%O >].
Local Notation "f <= g" := (lef f g).

Definition ltf f g := `[< ( x, (f x g x)%O) x, f x != g x >].
Local Notation "f < g" := (ltf f g).

Lemma ltf_def f g : (f < g) = (g != f) && (f g).
Proof.
apply/idP/andP ⇒ [fg|[gf fg]]; [split|apply/asboolP; split; [exact/asboolP|]].
- by apply/eqPgf; move: fg ⇒ /asboolP[fg] [x /eqP]; apply; rewrite gf.
- apply/asboolPx; rewrite le_eqVlt; move/asboolP : fg ⇒ [fg [y gfy]].
  by have [//|gfx /=] := boolP (f x == g x); rewrite lt_neqAle gfx /= fg.
- apply/not_existsPh.
  have : f =1 g by movex; have /negP/negPn/eqP := h x.
  by rewrite -funeqE; apply/nesym/eqP.
Qed.

Fact lef_refl : reflexive lef. Proof. by movef; apply/asboolPx. Qed.

Fact lef_anti : antisymmetric lef.
Proof.
movef g ⇒ /andP[/asboolP fg /asboolP gf]; rewrite funeqEx.
by apply/eqP; rewrite eq_le fg gf.
Qed.

Fact lef_trans : transitive lef.
Proof.
moveg f h /asboolP fg /asboolP gh; apply/asboolPx.
by rewrite (le_trans (fg x)).
Qed.

Definition porderMixin :=
  @LePOrderMixin _ lef ltf ltf_def lef_refl lef_anti lef_trans.

Canonical porderType := POrderType fun_display (aT T) porderMixin.

End FunOrder.

Section FunLattice.
Import Order.TTheory.
Variables (aT : Type) (d : unit) (T : latticeType d).
Implicit Types f g h : aT T.

Definition meetf f g := fun xOrder.meet (f x) (g x).
Definition joinf f g := fun xOrder.join (f x) (g x).

Lemma meetfC : commutative meetf.
Proof. movef g; apply/funextx; exact: meetC. Qed.

Lemma joinfC : commutative joinf.
Proof. movef g; apply/funextx; exact: joinC. Qed.

Lemma meetfA : associative meetf.
Proof. movef g h; apply/funextx; exact: meetA. Qed.

Lemma joinfA : associative joinf.
Proof. movef g h; apply/funextx; exact: joinA. Qed.

Lemma joinfKI g f : meetf f (joinf f g) = f.
Proof. apply/funextx; exact: joinKI. Qed.

Lemma meetfKU g f : joinf f (meetf f g) = f.
Proof. apply/funextx; exact: meetKU. Qed.

Lemma lef_meet f g : (f g)%O = (meetf f g == f).
Proof.
apply/idP/idP ⇒ [/asboolP f_le_g|/eqP <-].
- apply/eqP/funextx; exact/meet_l/f_le_g.
- apply/asboolPx; exact: leIr.
Qed.

Definition latticeMixin :=
  LatticeMixin meetfC joinfC meetfA joinfA joinfKI meetfKU lef_meet.

Canonical latticeType := LatticeType (aT T) latticeMixin.

End FunLattice.
Module Exports.
Canonical porderType.
Canonical latticeType.
End Exports.
End FunOrder.
Export FunOrder.Exports.

Lemma lefP (aT : Type) d (T : porderType d) (f g : aT T) :
  reflect ( x, (f x g x)%O) (f g)%O.
Proof. by apply: (iffP idP) ⇒ [fg|fg]; [exact/asboolP | apply/asboolP]. Qed.

Lemma meetfE (aT : Type) d (T : latticeType d) (f g : aT T) x :
  ((f `&` g) x = f x `&` g x)%O.
Proof. by []. Qed.

Lemma joinfE (aT : Type) d (T : latticeType d) (f g : aT T) x :
  ((f `|` g) x = f x `|` g x)%O.
Proof. by []. Qed.

Lemma iterfS {T} (f : T T) (n : nat) : iter n.+1 f = f \o iter n f.
Proof. by []. Qed.

Lemma iterfSr {T} (f : T T) (n : nat) : iter n.+1 f = iter n f \o f.
Proof. by apply/funeqP ⇒ ?; rewrite iterSr. Qed.

Lemma iter0 {T} (f : T T) : iter 0 f = id.
Proof. by []. Qed.