Library sset5

Theory of Sets EIII-1 Order relations. Ordered sets

Copyright INRIA (2009-2011) Apics Team (Jose Grimm).

Require Import ssreflect ssrbool eqtype ssrnat ssrfun.

Set Implicit Arguments.
Unset Strict Implicit.
Import Prenex Implicits.

Require Export sset4.


Import Correspondence.
Import Bfunction.
Module Border.

EIII-1-1 Definition of an order relation


We define antisymmetric_r for a relation and is_antisymmetric for a graph and refkexive_rr for a relation

Definition antisymmetric_r (r:Set -> Set -> Prop) :=
  forall x y, r x y -> r y x -> x = y.

Definition is_antisymmetric (r:Set) :=
 is_graph r & forall x y, related r x y -> related r y x -> x = y.

Definition reflexive_rr (r:Set -> Set -> Prop) :=
  forall x y, r x y -> (r x x & r y y).

We define an order and a preorder relation

Definition order_r (r:Set -> Set -> Prop) :=
  transitive_r r & antisymmetric_r r & reflexive_rr r.

Definition preorder_r (r:Set -> Set -> Prop) :=
  transitive_r r & reflexive_rr r.

Lemma equality_is_order: order_r(fun x y => x = y).
Proof.
split.
  by hnf; intuition; ue.
split;hnf; intuition.
Qed.

Lemma sub_is_order: order_r sub.
Proof.
split; first by move=> x y t xy yz; apply (sub_trans xy yz).
split; first by move=> x y xy yx; apply extensionality.
move=> x y; split; apply sub_refl.
Qed.

Definition opposite_relation (r:Set -> Set -> Prop) := fun x y => r y x.

Lemma opposite_is_preorder_r: forall r,
  preorder_r r -> preorder_r (opposite_relation r).
Proof.
rewrite /preorder_r/opposite_relation => r [tr rr].
split.
  by move=> x y z xy yz; apply (tr _ _ _ yz xy).
by move=> x y yx /=; move: (rr _ _ yx); intuition.
Qed.

Lemma opposite_is_order_r: forall r,
  order_r r -> order_r (opposite_relation r).
Proof.
move=> r [tr [ar rr]].
have pr: preorder_r r by hnf.
move: (opposite_is_preorder_r pr)=> [tr' rr'].
hnf; ee; rewrite/opposite_relation => x y xy yx.
by apply: (ar _ _ yx).
Qed.

An order is either a relation or a graph

Definition order_re (r:Set -> Set -> Prop) x :=
  order_r r & reflexive_r r x.

Definition order (r:Set) :=
  is_reflexive r & is_transitive r & is_antisymmetric r.

Lemma order_is_graph: forall r, order r -> is_graph r.
Proof. by move => r [[g _] _]. Qed.

Hint Resolve order_is_graph : fprops.

Definition opposite_order := inverse_graph.

Definition preorder (r: Set) :=
  is_reflexive r & is_transitive r.

Lemma preorder_graph : forall r, preorder r -> is_graph r.
Proof. by move => r [[g _] _]. Qed.

Lemma order_preorder : forall r, order r -> preorder r.
Proof. by move => r [tr [ar rr]]; hnf. Qed.

Lemma order_has_graph0: forall r x,
  order_re r x -> is_graph_of (graph_on r x) r.
Proof.
move=> r x [ [_ [_ rrr]] rr].
move => u v ; rewrite /related/graph_on.
apply iff_eq ; rewrite Z_rw; aw; last by intuition.
move=> ruv; split=>//; split; fprops.
move: (rrr _ _ ruv) => [ruu rvv].
by rewrite 2! rr.
Qed.

Lemma graph_on_rw3: forall r x u v,
  order_re r x ->
  related (graph_on r x) u v = r u v.
Proof. move=> r x u v hyp; symmetry; apply (order_has_graph0 hyp).
Qed.

Lemma order_has_graph: forall r x,
  order_re r x -> exists g, is_graph_of g r.
Proof.
move=> r x hyp; exists (graph_on r x).
by apply order_has_graph0.
Qed.

Lemma order_if_has_graph: forall r g,
  is_graph g -> is_graph_of g r ->
  order_r r -> order_re r (domain g).
Proof.
move=> r g gg gor or; split=>//.
move : gor; rewrite /is_graph_of/related => gor.
move => x; apply iff_eq; dw.
  move => [y]; rewrite -gor=> rxy.
  by move: or=> [_ [_ rr]]; move: (rr _ _ rxy); case.
by move=> h; exists x;rewrite - gor.
Qed.

Lemma order_if_has_graph2: forall r g,
  is_graph g -> is_graph_of g r ->
  order_r r -> order g.
Proof.
move=> r g gg gor or.
move: gor; rewrite /is_graph_of/related => gor.
move: or => [tr [ar rer]].
split.
  split=>//.
  move=> y; rewrite inc_substrate_rw /related // -gor.
  by case; move=> [z]; rewrite - gor => aux; move: (rer _ _ aux); intuition.
split; split=>//.
  move=> x y z; rewrite /related -3! gor; apply tr.
move=> x y; rewrite /related -2! gor; apply ar.
Qed.

Lemma order_has_graph2: forall r x,
  order_re r x -> exists g,
    g = graph_on r x &
    order g & (forall u v, r u v = related g u v).
Proof.
move=> r x or; exists (graph_on r x).
have gg: is_graph (graph_on r x) by apply graph_on_graph.
have gor: is_graph_of (graph_on r x) r by apply order_has_graph0.
move: or => [or1 or2].
ee; apply (order_if_has_graph2 gg gor or1).
Qed.

Lemma preorder_from_rel: forall r x,
  preorder_r r -> preorder (graph_on r x).
Proof.
move=> r x pr.
have gg: (is_graph (graph_on r x)) by apply graph_on_graph.
move: pr=> [tr rr].
split; split=>//.
  move=> y; rewrite inc_substrate_rw // /related.
  case; move=> [z]; rewrite 2! graph_on_rw0;
     move=> [yx [zx ryz]];move: (rr _ _ ryz); intuition.
move=> a b c; rewrite 3! graph_on_rw1.
move=> [ax [bx rab]] [_ [cx rbc]]; intuition; apply (tr _ _ _ rab rbc).
Qed.

Lemma order_from_rel: forall r x,
  order_r r -> order (graph_on r x).
Proof.
move=> r x or.
have po: preorder (graph_on r x) .
  by apply preorder_from_rel; red in or; hnf; intuition.
move: po => [tr rr].
hnf; intuition.
split; first by move: tr => [g _].
move=> a b; rewrite 2! graph_on_rw1.
move=> [ax [bx rab]] [_ [_ rba]].
by move: or=> [_ [ta _]]; apply ta.
Qed.

Lemma order_reflexivity_pr: forall r x u v,
  order_re r x -> r u v -> (inc u x & inc v x).
Proof.
move=> r x u v [ [_ [_ rrr]] rr] ruv.
by rewrite 2!rr; apply rrr.
Qed.

Lemma order_symmetricity_pr: forall r x u v,
  order_re r x -> (r u v & r v u) = (inc u x & inc v x & u = v).
Proof.
move=> r x u v or.
apply iff_eq.
  move=> [ruv rvu].
  move: (order_reflexivity_pr or ruv)=> [ux vx].
  by intuition; move: or => [[ _ [asy _]] _]; apply asy.
move=> [ux [vx]] ->.
by move: or=> [_ rr]; rewrite rr in vx.
Qed.

Lemma order_reflexivity: forall r a,
  order r -> inc a (substrate r) = related r a a.
Proof.
move=> r a [[_ or] _].
apply iff_eq => h; first by apply or.
substr_tac.
Qed.

Lemma order_antisymmetry: forall r a b,
  order r -> related r a b -> related r b a -> a = b.
Proof.
move=> r a b [ _ [_ [_]]]; apply.
Qed.

Lemma order_transitivity: forall r a b c,
  order r -> related r a b -> related r b c -> related r a c.
Proof.
by move=> r a b c [ _ [[ _ t] _]]; apply t.
Qed.

Lemma inter_rel_order : forall z,
  nonempty z -> (forall r, inc r z -> order r) ->
  order (intersection z).
Proof.
move=> z nez alo.
have ri: (is_reflexive (intersection z)) .
  apply inter_rel_reflexive=> //.
  by move=> r rz; move: (alo _ rz) => [re _].
split =>//.
split.
  apply inter_rel_transitive=> //.
  by move=> r rz; move: (alo _ rz) => [_ [tr _]].
split; first by move: ri => [g _].
move=> x y xi yi; move: nez => [t tz].
move: (intersection_forall xi tz)(intersection_forall yi tz).
by move: (alo _ tz)=> [ _ [ _ [_ ra] ]]; apply ra.
Qed.

Definition gle (r x y:Set) := related r x y.
Definition gge (r x y:Set) := gle r y x.
Definition glt (r x y:Set) := gle r x y & x <> y.
Definition ggt (r x y:Set) := gge r x y & x <> y.

Lemma lt_leq_trans : forall r x y z,
  order r -> glt r x y -> gle r y z -> glt r x z.
Proof.
move=> r x y z or [le1 nxy] le2; split.
  by apply order_transitivity with y.
dneg xz.
rewrite -xz in le2.
apply (order_antisymmetry or le1 le2).
Qed.

Lemma leq_lt_trans : forall r x y z,
  order r -> gle r x y -> glt r y z -> glt r x z.
Proof.
move=> r x y z or le1 [le2 nxy]; split.
  by apply order_transitivity with y.
dneg xz.
rewrite xz in le1.
apply (order_antisymmetry or le2 le1).
Qed.

Lemma lt_lt_trans: forall r a b c, order r ->
  glt r a b -> glt r b c -> glt r a c.
Proof. move=> r x y z or [le1 nxy] le2. apply (leq_lt_trans or le1 le2).
Qed.

Lemma not_le_gt: forall r x y, order r -> gle r x y -> glt r y x -> False.
Proof.
move=> r x y or le1 le2.
move: (leq_lt_trans or le1 le2) => [_ xx].
by elim xx.
Qed.

Ltac order_tac:=
  match goal with
    | H1: gle ?r ?x _ |- inc ?x (substrate ?r)
      => exact (inc_arg1_substrate H1)
    | H1: glt ?r ?x _ |- inc ?x (substrate ?r)
      => move: H1 => [H1 _] ; order_tac
    | H1:gle ?r _ ?x |- inc ?x (substrate ?r)
      => exact (inc_arg2_substrate H1)
    | H1:glt ?r _ ?x |- inc ?x (substrate ?r)
      => move: H1 => [H1 _]; order_tac
    | H: order ?r, H1: inc ?u (substrate ?r) |- related ?r ?u ?u
      => rewrite - order_reflexivity //
    | H: order ?r |- inc (J ?u ?u) ?r
       => change (related r u u); rewrite - order_reflexivity //
    | H: order ?r |- gle ?r ?u ?u
       => rewrite /gle -order_reflexivity //
    | H1: gle ?r ?x ?y, H2: gle ?r ?y ?x, H:order ?r |-
        ?x = ?y => exact (order_antisymmetry H H1 H2)
    | H:order ?r, H1:related ?r ?x ?y, H2: related ?r ?y ?x |- ?x = ?y
      => apply (order_antisymmetry H H1 H2)
    | H:order ?r, H1: inc (J ?x ?y) ?r , H2: inc (J ?y ?x) ?r |- ?x = ?y
      => apply (order_antisymmetry H H1 H2)
    | H:order ?r, H1:related ?r ?u ?v, H2: related ?r ?v ?w
      |- related ?r ?u ?w
      => apply (order_transitivity H H1 H2)
    | H:order ?r, H1:gle ?r ?u ?v, H2: gle ?r ?v ?w
      |- gle ?r ?u ?w
      => apply (order_transitivity H H1 H2)
    | H: order ?r, H1: inc (J ?u ?v) ?r, H2: inc (J ?v ?w) ?r |-
      inc (J ?u ?w) ?r
      => change (related r u w); apply (order_transitivity H H1 H2)
    | H1: gle ?r ?x ?y, H2: glt ?r ?y ?x, H: order ?r |- _
      => elim (not_le_gt H H1 H2)
    | H1:glt ?r ?x ?y, H2: glt ?r ?y ?x, H:order ?r |- _
      => destruct H1 as [H1 _] ; elim (not_le_gt H H1 H2)
    | H1:order ?r, H2:glt ?r ?x ?y, H3: gle ?r ?y ?z |- glt ?r ?x ?z
       => exact (lt_leq_trans H1 H2 H3)
    | H1:order ?r, H2:gle ?r ?x ?y, H3: glt ?r ?y ?z |- glt ?r ?x ?z
       => exact (leq_lt_trans H1 H2 H3)
    | H1:order ?r, H2:glt ?r ?x ?y, H3: glt ?r ?y ?z |- glt ?r ?x ?z
       => exact (lt_lt_trans H1 H2 H3)
    | H1:order ?r, H2:gle ?r ?x ?y |- glt ?r ?x ?y
       => split =>//
    | H:glt ?r ?x ?y |- gle ?r ?x ?y
       => by move: H=> []
end.

Lemma le_antisym: forall r a b,
  order r -> gle r a b -> gle r b a -> a = b.
Proof.
move=> r a b [ _ [_ [_]]]; apply.
Qed.

Lemma order_is_order: forall r,
  order r -> order_r (related r).
Proof.
move => r [[g rr] [[_ t] [_ s]]].
hnf; ee; move=> x y xy.
by move: (rr _ (inc_arg1_substrate xy)) (rr _ (inc_arg2_substrate xy)).
Qed.

Lemma substrate_opposite_order: forall r,
  order r -> substrate (opposite_order r) = substrate r.
Proof.
move=> r or.
have gr: is_graph r by fprops.
have go: is_graph (opposite_order r) by fprops.
set_extens x; do 2 rewrite inc_substrate_rw //.
case; move=> [y]; aw => h; [ right | left ]; ex_tac.
case; move=> [y]; [ right | left ]; exists y; aw.
Qed.

Hint Rewrite substrate_opposite_order : aw.

Lemma substrate_domain_order: forall f,
  order f -> substrate f = domain f.
Proof.
move=> f or.
set_extens x => xs.
  have Jf: inc (J x x) f by order_tac.
  by rewrite /domain; aw; exists (J x x); aw; auto.
by rewrite /substrate; apply union2_first.
Qed.

Lemma opposite_is_order: forall r,
  order r -> order (opposite_order r).
Proof.
move=> r or;
set (g:= opposite_order r).
have gg: is_graph g by rewrite /g; fprops.
have go: is_graph_of g (opposite_relation (related r)).
  move=> x y.
  by rewrite/ opposite_relation/g/related ; aw.
have ori: order_r (opposite_relation (related r)).
  by apply opposite_is_order_r; apply order_is_order.
by apply (order_if_has_graph2 gg go ori).
Qed.

Hint Resolve opposite_is_order: fprops.

Lemma diagonal_order : forall x,
  order (identity_g x).
Proof.
move=> x.
move: (diagonal_equivalence x) => [ir [it _]].
hnf; intuition.
split; first by move: ir=> [h _].
move=> a b; rewrite /related;aw. intuition.
Qed.

Order induced by a relation on a set

Lemma order_from_rel1: forall r x,
  transitive_r r ->
  (forall u v, inc u x -> inc v x -> r u v -> r v u -> u = v) ->
  (forall u, inc u x -> r u u) ->
  order (graph_on r x).
Proof.
move=> r x tr sy re.
have go: (graph_on r x = graph_on (fun a b => inc a x & inc b x & r a b) x).
  rewrite /graph_on.
  set_extens w; rewrite Z_rw Z_rw; aw; intuition.
rewrite go; apply order_from_rel.
split.
  move => a b c [ax [bx rab]] [_ [cx rbc]]; intuition; apply(tr _ _ _ rab rbc).
split=>//.
  by move=> a b [ax [bx rab]] [_ [cx rbc]]; apply sy.
by move=> a b [ax [bx rab]]; intuition.
Qed.

Lemma substrate_graph_on: forall (r:Set -> Set -> Prop) x,
  (forall a, inc a x -> r a a) ->
  substrate (graph_on r x) = x.
Proof.
move=> r x rr.
apply extensionality; first by apply graph_on_substrate.
move => t xt; move: (rr _ xt) => rtt.
have aux: (related (graph_on r x) t t).
  by rewrite /graph_on /related Z_rw; aw; fprops.
substr_tac.
Qed.

Inclusion order on the powerset of a set, or a part of it
Definition inclusion_suborder (b:Set) :=
  graph_on sub b.
Definition inclusion_order (a:Set) := inclusion_suborder (powerset a).

Lemma subinclusion_is_order: forall a,
  order (inclusion_suborder a).
Proof.
rewrite /inclusion_suborder => a.
apply order_from_rel; apply sub_is_order.
Qed.

Lemma inclusion_is_order: forall a,
  order (inclusion_order a).
Proof.
rewrite /inclusion_order => a. apply subinclusion_is_order.
Qed.

Lemma substrate_subinclusion_order:forall a,
  substrate (inclusion_suborder a) = a.
Proof.
rewrite /inclusion_suborder => a.
apply substrate_graph_on.
move=> x; fprops.
Qed.

Lemma substrate_inclusion_order: forall a,
  substrate (inclusion_order a) = powerset a.
Proof. rewrite /inclusion_order=> a. apply substrate_subinclusion_order.
Qed.

Lemma subinclusion_order_rw: forall a u v ,
  related (inclusion_suborder a) u v = (inc u a & inc v a & sub u v).
Proof. by move=> a u v; rewrite /inclusion_suborder graph_on_rw1.
Qed.

Lemma inclusion_order_rw: forall a u v,
  related (inclusion_order a) u v = (sub u a & sub v a & sub u v).
Proof. move => a u v; rewrite /inclusion_order subinclusion_order_rw; aw.
Qed.

Hint Rewrite substrate_inclusion_order : aw.
Hint Rewrite substrate_subinclusion_order : aw.
Hint Resolve inclusion_is_order subinclusion_is_order: fprops.
Hint Rewrite subinclusion_order_rw inclusion_order_rw: aw.

Extension order on partial functions from x to y

Definition extension_order x y :=
  graph_on extends (set_of_sub_functions x y).

Hint Rewrite set_of_sub_functions_rw : bw.

Lemma extends_in_prop: forall x y,
  order_r extends &
  (forall u, inc u (set_of_sub_functions x y) -> extends u u).
Proof.
move=> x y; rewrite /extends.
split; last by move=> u; bw; intuition.
split.
  move=> a b c [fb [fa [gba tba]]] [fc [_ [gcb tcb]]].
  intuition.
    by apply sub_trans with (graph b).
  by apply sub_trans with (target b).
split; last by red; intuition.
move=> a b [fb [fa [gba tba]]] [_ [_ [gab tab]]].
have grab: graph a = graph b by apply extensionality.
have tgab: target a = target b by apply extensionality.
by apply function_exten1.
Qed.

Lemma extension_is_order: forall x y,
  order (extension_order x y).
Proof.
rewrite /extension_order=> x y.
move: (extends_in_prop x y) => [p1 _].
by apply order_from_rel.
Qed.

Lemma substrate_extension_order: forall x y,
  substrate (extension_order x y) = (set_of_sub_functions x y).
Proof.
rewrite /extension_order => x y.
by move: (extends_in_prop x y)=> [_ p1]; apply substrate_graph_on.
Qed.

Lemma extension_order_rw : forall E F f g,
  gle (extension_order E F) g f =
  (inc g (set_of_sub_functions E F) & inc f (set_of_sub_functions E F)
    & extends g f).
Proof.
by move=> E F f g; rewrite /gle/related /extension_order graph_on_rw0.
Qed.

Lemma extension_order_pr1 : forall E F f g,
  gle (extension_order E F) g f =
  (inc g (set_of_sub_functions E F) & inc f (set_of_sub_functions E F)
    & sub (graph f) (graph g)).
Proof.
move=> E F f g; rewrite extension_order_rw /extends.
apply iff_eq; first by intuition.
move=> [p1 [p2 sh]]; split=>//; split=>//.
move: p1 p2; bw; move=> [fg [_ tg]] [ff [_ tf]]; rewrite tg tf.
fprops.
Qed.

Lemma extension_order_pr2 : forall E F f g,
  gle (opposite_order (extension_order E F)) g f =
  (inc g (set_of_sub_functions E F) & inc f (set_of_sub_functions E F)
    & sub (graph g) (graph f)).
Proof.
move=> E F f g. rewrite /gle/related; aw.
rewrite -[inc (J f g) (extension_order E F)]/(gle (extension_order E F) f g).
rewrite extension_order_pr1.
by apply iff_eq; intuition.
Qed.

Hint Rewrite extension_order_rw: aw.
Hint Resolve extension_is_order: fprops.

Lemma extension_order_pr : forall E F f g x,
  gle (opposite_order (extension_order E F)) f g ->
  inc x (source f) -> W x f = W x g.
Proof.
move=> E F f g x; rewrite /opposite_order; bw; aw.
move => [gs [fs et]] xsf.
apply (W_extends et xsf).
Qed.

Hint Rewrite substrate_extension_order: aw.

Example 4. Order between partitions on a set

Definition set_of_partition_set x :=
  Zo(powerset(powerset x)) (fun z => partition z x).

Definition partition_fun_of_set y x:=
  canonical_injection y (powerset x).

Lemma partition_set_in_double_powerset: forall y x,
  partition y x -> inc y (powerset (powerset x)).
Proof.
move=> y x [pa _]; aw; move => t ty; aw; move => z zt.
move: (union_inc zt ty); ue.
Qed.

Lemma set_of_partition_rw: forall x y,
  inc y (set_of_partition_set x) = partition y x.
Proof.
move=> x y; rewrite /set_of_partition_set.
apply iff_eq; rewrite Z_rw; intuition.
by apply partition_set_in_double_powerset.
Qed.

Lemma pfs_function: forall y x,
  partition y x -> is_function (partition_fun_of_set y x).
Proof.
move=> y x pa; rewrite/partition_fun_of_set.
by apply ci_function; apply powerset_sub; apply partition_set_in_double_powerset.
Qed.

Lemma pfs_W: forall y x a,
  partition y x -> inc a y ->
  W a (partition_fun_of_set y x) = a.
Proof.
rewrite /partition_fun_of_set=> y x a pa ay.
by apply ci_W => //; apply powerset_sub; apply partition_set_in_double_powerset.
Qed.

Lemma pfs_partition: forall y x,
  partition y x -> partition_fam (graph (partition_fun_of_set y x)) x.
Proof.
move=> y x pa.
move: (pfs_function pa) => fpa.
move: (pa)=> [ux [_ di]].
have Wp: forall i, inc i y -> V i (graph (partition_fun_of_set y x)) = i.
  by move => i iy; apply pfs_W.
have dg: domain (graph (partition_fun_of_set y x)) = y.
  by rewrite f_domain_graph// /partition_fun_of_set /canonical_injection; aw.
split; fprops; split.
  move=> i j; rewrite dg => iy jy; rewrite Wp // Wp //.
  by apply di.
set_extens v => vs.
   move: (unionb_exists vs)=> [w]; rewrite dg.
   move=> [wy]; rewrite Wp // -ux => vW; union_tac.
rewrite -ux in vs; move: (union_exists vs)=> [w [vw wy]].
by srw; exists w; rewrite dg (Wp _ wy).
Qed.

All these lemmas say that, essentially, a partition is a partition set
Definition coarser x := graph_on coarser_c (set_of_partition_set x).

Lemma coarser_related: forall x y y',
  related (coarser x) y y' =
  (partition y x & partition y' x & coarser_c y y').
Proof.
rewrite /coarser/graph_on/related=> x y y'.
apply iff_eq; rewrite Z_rw product_pair_rw 2! set_of_partition_rw ; aw;
  intuition.
Qed.

Lemma coarser_related_bis: forall x y y',
  related (coarser x) y y' =
  (partition y x & partition y' x &
    forall a, inc a y' -> exists b, inc b y & sub a b).
Proof. by move=> x y y'; rewrite coarser_related.
Qed.

Lemma coarser_substrate : forall x,
  substrate (coarser x) = set_of_partition_set x.
Proof.
move=> x; rewrite /coarser.
apply extensionality; first by apply graph_on_substrate.
move => t ts.
apply inc_arg1_substrate with t.
rewrite /related/graph_on Z_rw product_pair_rw; aw.
intuition; apply coarser_reflexive.
Qed.

Lemma coarser_order : forall x, order (coarser x).
Proof.
move=> x.
have gc: is_graph (coarser x) by rewrite /coarser; apply graph_on_graph.
hnf; intuition; split=>//.
    move=> y; rewrite coarser_related coarser_substrate set_of_partition_rw.
    intuition; apply coarser_reflexive.
  move=> a b c; rewrite 3! coarser_related.
  by intuition; apply coarser_transitive with b.
move=> a b;rewrite 2!coarser_related. intuition.
by apply coarser_antisymmetric with x.
Qed.

Lemma smallest_partition_is_smallest: forall x y,
  nonempty x ->
  partition y x -> related (coarser x) (smallest_partition x) y.
Proof.
move=> x y nex pa; rewrite coarser_related.
intuition; first by apply partition_smallest.
move=> u uy; rewrite /smallest_partition.
ex_tac.
move: (partition_set_in_double_powerset pa); aw => yp.
by apply powerset_sub; apply yp.
Qed.

Lemma largest_partition_is_largest: forall x y,
  partition y x -> related (coarser x) y (largest_partition x).
Proof.
move=> x y pa; rewrite coarser_related.
intuition; first by apply partition_largest.
move=> u; rewrite /largest_partition; aw.
move=> [b [bx]] <-.
move: pa=> [ux _]; move: bx; rewrite -ux; srw.
by move=> [z [bz zy]]; ex_tac; move=> t; aw=> ->.
Qed.

Definition partition_relation_set y x :=
  partition_relation (partition_fun_of_set y x) x.

Lemma prs_is_equivalence: forall y x,
  partition y x -> is_equivalence (partition_relation_set y x).
Proof.
move=> y x pa; rewrite /partition_relation_set.
apply partition_is_equivalence.
  by apply pfs_function.
by apply pfs_partition.
Qed.

Definition partition_relation_set_aux y x :=
  Zo (powerset (coarse x)) (fun z => exists a, inc a y & z = coarse a).

Lemma partition_relation_set_pr1: forall y x a,
  partition y x ->
  inc a y -> inc (coarse a) (partition_relation_set_aux y x).
Proof.
move=> y x a pa ay.
move: (partition_set_in_double_powerset pa).
rewrite powerset_inc_rw=> yp.
have ap: (inc a (powerset x)) by apply yp.
rewrite /partition_relation_set_aux /coarse Z_rw; aw.
split; first by awi ap; apply product_monotone.
ex_tac.
Qed.

Lemma partition_relation_set_pr: forall y x,
  partition y x ->
  partition_relation_set y x =
  union (partition_relation_set_aux y x).
Proof.
move=> y x pa.
have pfa: (partition_fam (graph(partition_fun_of_set y x)) x).
  by apply pfs_partition.
move: (partition_set_in_double_powerset pa) => ypp.
have sp:source (partition_fun_of_set y x) = y.
  by rewrite /partition_fun_of_set /canonical_injection; aw.
awi ypp.
have p1: is_function (partition_fun_of_set y x) by apply pfs_function.
have p2: partition_fam (graph (partition_fun_of_set y x)) x.
  by move: (prs_is_equivalence pa); fprops.
have p3: is_graph (partition_relation (partition_fun_of_set y x) x).
  by rewrite /partition_relation; apply graph_on_graph.
have p4:is_graph (union (partition_relation_set_aux y x)).
  move=> t tp; move: (union_exists tp); rewrite/partition_relation_set_aux.
  move=> [z]; rewrite Z_rw powerset_inc_rw /coarse.
  by move=> [tz [zp _]]; move: (zp _ tz); aw; intuition.
rewrite /partition_relation_set graph_exten //.
move=> u v; rewrite partition_relation_pr /in_same_coset // sp.
apply iff_eq.
  move=> [i [iy []]]; rewrite pfs_W // /related => ui vi.
  apply union_inc with (coarse i).
    rewrite/coarse; fprops.
  by apply partition_relation_set_pr1.
rewrite /related; srw; move=> [z [Jz]].
rewrite/ partition_relation_set_aux Z_rw; move => [zp [a [ay zc]]].
ex_tac.
by rewrite pfs_W //; move: Jz; rewrite zc /coarse product_pair_rw.
Qed.

Lemma sub_partition_relation_set_coarse: forall y x,
  partition y x -> sub (partition_relation_set y x) (coarse x).
Proof.
move=> y x pa; rewrite partition_relation_set_pr //.
move=> t tu.
move: (union_exists tu) => [z [tz]].
rewrite /partition_relation_set_aux Z_rw.
by move=> [zp _ ]; awi zp; apply zp.
Qed.

Lemma nondisjoint :forall a b c, inc a b -> inc a c -> disjoint b c -> False.
Proof.
by move=> a b c ab ac dg; red in dg; empty_tac1 a; apply intersection2_inc.
Qed.

Lemma partition_relation_set_order: forall x y y',
  partition y x -> partition y' x ->
  sub (partition_relation_set y' x)(partition_relation_set y x) =
  related (coarser x) y y'.
Proof.
move=> x y y' pa pa'.
do 2 rewrite partition_relation_set_pr //.
rewrite coarser_related /coarser_c; apply iff_eq.
  move => suu; split=>//; split=>//; move=> a ay.
  have nea: (nonempty a) by move: pa'=> [_ [ne _]]; apply ne.
  move: nea=> [b ba].
  have p: (forall u,inc u a -> exists c, (inc c y & inc b c & inc u c)).
    move=> u ua.
    have q: (inc (J b u) (union (partition_relation_set_aux y' x))).
      apply union_inc with (coarse a)=>//; rewrite /coarse; fprops.
      apply(partition_relation_set_pr1 pa' ay).
    move: (union_exists (suu _ q)) => [z [Jbb]].
    rewrite /partition_relation_set_aux Z_rw; move=> [zp [c [cy zc]]].
    by ex_tac; move: Jbb; rewrite zc /coarse product_pair_rw.
  move: (p _ ba) => [c [cy [bc _]]].
  ex_tac.
  move=> d da.
  move: (p _ da)=> [f [fy [bf df]]].
  move:pa => [_ [_ aux ]]; case:(aux _ _ cy fy).
    by move => ->.
  by move=> dcf;elim (nondisjoint bc bf dcf).
move=> [_ [_ hyp]] z zu.
move: (union_exists zu)=> [t [zt]].
rewrite /partition_relation_set_aux Z_rw; move => [tp [a [ay tc]]].
move: (hyp _ ay)=> [b [iby ab]].
apply union_inc with (coarse b); last by apply partition_relation_set_pr1.
have scab: (sub (coarse a) (coarse b)) by apply product_monotone.
apply scab; ue.
Qed.

Lemma partition_relation_set_order_antisymmetric: forall x y y',
  partition y x -> partition y' x ->
  (partition_relation_set y' x = partition_relation_set y x) ->
   y = y'.
Proof.
suff: (forall x y y',
  partition y x -> partition y' x ->
  (partition_relation_set y' x = partition_relation_set y x) ->
  sub y y').
  move=> aux a b c p1 p2 eq. apply extensionality.
  apply (aux _ _ _ p1 p2 eq). apply (aux _ _ _ p2 p1 (sym_eq eq)).
move=> x y y' pa pa'.
rewrite (partition_relation_set_pr pa) (partition_relation_set_pr pa').
move=> eq a ay.
move: (partition_relation_set_pr1 pa ay)=> cpy.
have nea: (nonempty a) by move: pa=> [_ [ne _]]; apply ne.
move: nea=> [b ba].
have p: (forall u v,inc u a -> inc v a->
    exists c, (inc c y' & inc u c & inc v c)).
   move=> u v ua va.
   have p1: inc (J u v) (coarse a) by rewrite /coarse; fprops.
   have p2: (inc (J u v) (union (partition_relation_set_aux y' x))).
   rewrite eq; union_tac.
   move: (union_exists p2) => [z [Jbb]].
   rewrite /partition_relation_set_aux Z_rw; move=> [zp [c [cy zc]]].
   by ex_tac; move: Jbb; rewrite zc /coarse product_pair_rw.
move: (p _ _ ba ba) => [c [cy [bc _]]].
suff: a = c by move=> ->.
set_extens t => ta.
  move: (p _ _ ta ta) => [c' [cy' [tc' _]]].
  move: (p _ _ ba ta) => [c'' [cy'' [bc'' tc'']]].
  move: pa' => [_ [_ aux ]]; case:(aux _ _ cy cy'').
    move => ->; case:(aux _ _ cy'' cy'); first by move => ->.
    by move=> dcf;elim (nondisjoint tc'' tc' dcf).
  by move=> dcf;elim (nondisjoint bc bc'' dcf).
have p1: (inc (J b t) (coarse c)) by rewrite/coarse; fprops.
have p2: (inc (J b t) (union (partition_relation_set_aux y' x))).
  apply union_inc with (coarse c) => //.
  apply (partition_relation_set_pr1 pa' cy).
move: p2; rewrite eq /partition_relation_set_aux => p2.
move: (union_exists p2)=> [z [Jbb]]; rewrite Z_rw;move=> [zp [d [dy zx]]].
move: Jbb; rewrite zx /coarse; aw; move=> [_ [bd td]].
move: pa => [_ [_ aux ]]; case:(aux _ _ ay dy).
    by move=> ->.
move=> dfc; elim (nondisjoint ba bd dfc).
Qed.

Lemma preorder_prop1: forall g,
  is_graph g ->
  sub (identity_g (substrate g)) g -> sub (compose_graph g g) g ->
  compose_graph g g = g.
Proof.
move=> g gg sig scg.
apply extensionality; first by aw.
move => x xg; aw; split; first by apply gg.
exists (Q x); rewrite (gg _ xg); ee; apply sig; aw; ee; substr_tac.
Qed.

Theorem order_pr: forall r,
  order r =
    (compose_graph r r = r &
    intersection2 r (opposite_order r) = identity_g (substrate r)).
Proof.
move=> r.
apply iff_eq.
  move=> or.
  set (d:= identity_g (substrate r)).
  have i2: (intersection2 r (opposite_order r) = d).
    set_extens t.
      aw; move=> [tr].
      rewrite inverse_graph_rw;fprops; move=> [pt Jr].
      have px: (J (P t) (Q t) = t) by aw.
      rewrite - px in tr.
      move: (order_antisymmetry or tr Jr)=> pq.
      by rewrite /d -px pq; aw; split=>//; substr_tac.
    rewrite/d; rewrite inc_diagonal_rw; move=> [pt [Ps pq]].
    have Jpq: (J (P t) (Q t) = t) by aw.
    rewrite -Jpq -pq.
    have rpp: (related r (P t) (P t)) by order_tac.
    by apply intersection2_inc=>//; aw.
  have gr: (is_graph r) by fprops.
  have sd: (sub d r) by rewrite -i2; apply intersection2sub_first.
  have sc: (sub (compose_graph d r) (compose_graph r r)).
    by apply composition_increasing; fprops.
  have crrr: (sub (compose_graph r r) r).
    by move: or => [_[t _]];rewrite -idempotent_graph_transitive //.
  by split =>//; apply preorder_prop1.
move=> [crrr iro].
have gr: is_graph r by rewrite -crrr; apply composition_is_graph.
hnf; intuition; split=>//.
- move=> y ys.
  have: (inc (J y y) (identity_g (substrate r))) by aw; auto.
  by rewrite -iro; move=> Ji; apply (intersection2_first Ji).
- have: sub (compose_graph r r) r by rewrite crrr; fprops.
  by rewrite -idempotent_graph_transitive //; move=> [_].
- move => x y rxy ryx.
  have p1: (inc (J x y) (opposite_order r)) by aw.
  have: (inc (J x y) (identity_g (substrate r))).
    by rewrite - iro; apply intersection2_inc.
  aw ; intuition.
Qed.

Preorder relations


Lemma preorder_is_preorder: forall r,
  preorder r -> preorder_r (related r).
Proof.
move=> r [[gr rr] [_ tr]].
split=>//.
move=> x y rxy.
have p1: (inc x (substrate r)) by substr_tac.
have p2: (inc y (substrate r)) by substr_tac.
by split; apply rr.
Qed.

Lemma opposite_is_preorder1: forall r,
  preorder r -> preorder (opposite_order r).
Proof.
move=> r [[gr rr] [_ tr]].
have go: (is_graph (opposite_order r)) by fprops.
have ss: (sub (substrate (opposite_order r)) (substrate r)).
  move=> t; rewrite inc_substrate_rw //inc_substrate_rw //.
  by case;move=> [y]; aw =>h; [ right | left ] ;ex_tac.
split.
  by split=>//; move=> y ys; red; aw; apply rr; apply ss.
split=>//; move => x y z; rewrite /related;aw.
move =>p1 p2; apply (tr _ _ _ p2 p1).
Qed.

Lemma equivalence_preorder: forall r,
  preorder_r r ->
  equivalence_r (fun x y => r x y & r y x).
Proof.
move=> r [ tr _]; split; first by move=> x y [rxy ryx].
move=> x y z [xy yx] [yz zy].
split; [ apply (tr _ _ _ xy yz) | apply (tr _ _ _ zy yx)].
Qed.

Lemma compatible_equivalence_preorder: forall r,
  let s := (fun x y => r x y & r y x) in
  preorder_r r -> forall x y x' y', r x y -> s x x' -> s y y' -> r x' y'.
Proof.
move=> r s [tr _] x y x' y' rxy [rxx' rx'x] [ryy' ry'y].
move: (tr _ _ _ rx'x rxy)=> rx'y.
apply (tr _ _ _ rx'y ryy').
Qed.

Equivalence relation associated to a preorder

Definition equivalence_associated_o r:= intersection2 r (opposite_order r).

Lemma equivalence_preorder1: forall r,
  preorder r ->
  is_equivalence (equivalence_associated_o r).
Proof.
move=> r pr.
rewrite /equivalence_associated_o.
have gr: is_graph r by apply preorder_graph.
have gi: is_graph (opposite_order r) by fprops.
move: (inter2_is_graph2 gr (x:=r)) => gi2.
have gi3: is_graph (intersection2 r (opposite_order r)).
  by move=> x xi; apply (gr _ (intersection2_first xi)).
apply symmetric_transitive_equivalence.
  split=>//; move=> x y; rewrite /related; aw; intuition.
split=>//; move=> x y z;rewrite /related; aw.
move=> [xy yx][yz zy]; move: pr=> [_ [_ tr]].
split; [apply (tr _ _ _ xy yz) | apply (tr _ _ _ zy yx) ].
Qed.

Lemma substrate_equivalence_associated_o: forall r,
  preorder r ->
  substrate (equivalence_associated_o r) = substrate r.
Proof.
move=> r pr.
move: (equivalence_preorder1 pr)=> eq.
set_extens x => xs.
  move: (reflexivity_e eq xs).
  rewrite/related /equivalence_associated_o=> h.
  by move:(intersection2_first h)=> h1; substr_tac.
move: pr=> [[gr rr] _].
move: (rr _ xs); rewrite /related=> Js.
have Je: (inc (J x x) (equivalence_associated_o r)).
  rewrite /equivalence_associated_o; apply intersection2_inc =>//; aw.
substr_tac.
Qed.

Lemma compatible_equivalence_preorder1: forall r u v x y,
  preorder r -> related r x y ->
  related (equivalence_associated_o r) x u ->
  related (equivalence_associated_o r) y v->
  related r u v.
Proof.
rewrite /related /equivalence_associated_o=> r u v x y pr rxy.
aw.
move: pr => [_ [_ tr]] [_ ux] [yv _].
move: (tr _ _ _ ux rxy)=> uy.
apply (tr _ _ _ uy yv).
Qed.

Lemma preorder_prop: forall g,
  is_graph g ->
  preorder g = (sub (identity_g (substrate g)) g & sub (compose_graph g g) g).
Proof.
move=> g gg.
apply iff_eq.
  move=> [[_ rr] tr].
  split; move=> x.
  rewrite inc_diagonal_rw; move=> [px [Px pq]].
    have : (J (P x) (Q x) = x) by aw.
    by move=> <-; rewrite -pq; apply rr.
  rewrite idempotent_graph_transitive // in tr.
  apply tr.
move=> [s1 s2].
split.
  split=>//.
  move=> y ys; rewrite /related; apply s1.
  by rewrite inc_diagonal_rw; aw; fprops.
by rewrite idempotent_graph_transitive.
Qed.

Lemma preorder_prop2: forall g,
  preorder g -> compose_graph g g = g.
Proof.
move=> g pg.
have gg: is_graph g by apply preorder_graph.
rewrite (preorder_prop gg) in pg.
by apply preorder_prop1; intuition.
Qed.

Lemma preorder_reflexivity: forall r a,
  preorder r -> inc a (substrate r) = related r a a.
Proof.
move=> r a [[g pr] _].
apply iff_eq; first by apply pr.
move=> h; substr_tac.
Qed.

Compare with fun_on_quotients
Definition order_associated (r:Set) :=
  let s := (graph(canon_proj (equivalence_associated_o r))) in
  compose_graph (compose_graph s r) (opposite_order s).

Lemma order_associated_graph: forall r,
  is_graph (order_associated r).
Proof.
rewrite /order_associated=> r; apply composition_is_graph.
Qed.

Lemma compose3_related: forall s r u v,
  related (compose_graph (compose_graph s r) (opposite_order s)) u v =
  exists x, exists y, related s x u & related s y v & related r x y.
Proof.
move=> s r u v; rewrite compose_related ;apply iff_eq.
  move=> [y []]; aw; bw; move=> yu [z [yz zv]].
  by exists y; exists z.
move=> [x [y [xu [yv xy]]]]; exists x.
aw; bw; ee.
by exists y.
Qed.

Lemma order_associated_related1: forall r u v,
  preorder r->
  related (order_associated r) u v =
  ( inc u (quotient (equivalence_associated_o r)) &
    inc v (quotient (equivalence_associated_o r)) &
    exists x, exists y, inc x u & inc y v & related r x y).
Proof.
move=> r u v pr.
set (s:= equivalence_associated_o r).
set (p:= graph (canon_proj s)).
rewrite /order_associated -/s -/p.
have es:is_equivalence s by rewrite /s; apply equivalence_preorder1.
rewrite compose3_related.
apply iff_eq.
  move=> [x [y [xu [yv xy]]]].
  have fc: is_function (canon_proj s) by apply canon_proj_function.
  move: (inc_pr1graph_source fc xu); aw => xs.
  move: (inc_pr1graph_source fc yv); aw => ys.
  have: (u = W x (canon_proj s)) by symmetry; apply W_pr=>//.
  have: (v = W y (canon_proj s)) by symmetry; apply W_pr=>//.
  rewrite canon_proj_W // canon_proj_W //.
  move=> -> ->; ee.
  exists x; exists y; split; first by apply inc_itself_class.
  by split=>//; apply inc_itself_class.
move=> [uq [vq [x [y [xu [yv xy]]]]]].
exists x; exists y.
split.
  rewrite /p/related related_graph_canon_proj //.
  apply (inc_in_quotient_substrate es xu uq).
split =>//.
rewrite /p/related related_graph_canon_proj //.
apply (inc_in_quotient_substrate es yv vq).
Qed.

Lemma order_associated_related2: forall r u v,
  preorder r->
  related (order_associated r) u v =
  ( inc u (quotient (equivalence_associated_o r)) &
    inc v (quotient (equivalence_associated_o r)) &
    forall x y, inc x u -> inc y v -> related r x y).
Proof.
move=> r u v pr; rewrite order_associated_related1 //.
set (s:= equivalence_associated_o r).
have es: is_equivalence s by rewrite /s; apply equivalence_preorder1.
apply iff_eq.
  move=> [uq [vq [x [y [xu [yv xy]]]]]].
  ee.
  move=> a b au bv.
  have xa: (related s x a).
    by rewrite in_class_related //; exists u; intuition; rewrite - inc_quotient.
  have yb: (related s y b).
    by rewrite in_class_related //; exists v; intuition; rewrite - inc_quotient.
  apply (compatible_equivalence_preorder1 pr xy xa yb).
move => [uq [vq hyp]]; split =>//; split=>//.
move: (non_empty_in_quotient es uq)=> [x xu].
move: (non_empty_in_quotient es vq)=> [y yv].
by exists x; exists y ; auto.
Qed.

Lemma eao_related: forall r a b,
 related (equivalence_associated_o r) a b -> related r a b.
Proof.
by move=> r a b; rewrite/equivalence_associated_o/related; aw; case.
Qed.

Lemma order_associated_substrate: forall r,
  preorder r-> substrate (order_associated r) =
  (quotient (equivalence_associated_o r)).
Proof.
move=> r pr.
have ea:is_equivalence (equivalence_associated_o r).
  by apply equivalence_preorder1.
set eao:= (equivalence_associated_o r).
have prop: forall a b, inc (J a b) (order_associated r)->
  (inc a (quotient eao) & inc b (quotient eao)).
  move=> a b Jab.
  have: (related (order_associated r) a b) by done.
  by rewrite order_associated_related2//; intuition.
set_extens x.
  rewrite inc_substrate_rw; last by apply order_associated_graph.
  by case; move=> [y yp]; move: (prop _ _ yp); intuition.
move=> xq.
suff: (related (order_associated r) x x) by move => w; substr_tac.
rewrite order_associated_related2 //; ee.
move=> z y zx yx.
have: (related (equivalence_associated_o r) z y).
  rewrite in_class_related //; exists x.
  by intuition; rewrite - inc_quotient.
apply eao_related.
Qed.

Lemma order_associated_order: forall r,
  preorder r-> order (order_associated r).
Proof.
move=> r pr.
have go: is_graph (order_associated r) by apply order_associated_graph.
have eea:is_equivalence (equivalence_associated_o r)
  by apply equivalence_preorder1.
have gr: is_graph r by move: pr=> [[]].
hnf; intuition;split =>//.
- move=> y; rewrite order_associated_substrate // => ys.
  move: pr=> [rr rt].
  rewrite order_associated_related2; split=>//; split=>//.
  move=> x z xy zy.
  have: (related (equivalence_associated_o r) x z).
     by rewrite in_class_related//; exists y; split=>//; rewrite - inc_quotient.
   apply eao_related.
- move=> x y z; do 3 rewrite order_associated_related2 //.
  move=>[xq [yq rxy]][_ [zq ryz]]; split=>//; split=>//.
  move => a b ax bz; move: (non_empty_in_quotient eea yq)=> [c cy].
  move:pr => [_ [_ tr]].
  apply (tr _ _ _ (rxy _ _ ax cy) (ryz _ _ cy bz)).
- move=> x y; do 2 rewrite order_associated_related2 //.
  move=> [xq [yq rxy]][_ [_ ryx]].
  move: (non_empty_in_quotient eea xq)=> [a ax].
  move: (non_empty_in_quotient eea yq)=> [b biy].
  move: (rxy _ _ ax biy)(ryx _ _ biy ax)=> rab rba;
  have reab: (related (equivalence_associated_o r) a b).
    rewrite /related /equivalence_associated_o; apply intersection2_inc =>//.
    aw.
  move: xq yq; srw => // xq yq.
  have bx: inc b x.
    move: xq; rewrite is_class_rw //.
    by move=> [_ [_ h]]; rewrite -(h a b ax) in reab.
  case (class_dichot xq yq) =>// hyp.
  elim (nondisjoint bx biy hyp).
Qed.

Lemma order_associated_pr: forall r,
  preorder r ->
  order_associated r = image_by_fun (
    ext_to_prod(canon_proj (equivalence_associated_o r))
    (canon_proj (equivalence_associated_o r))) r.
Proof.
move=> r pr.
set (s:= equivalence_associated_o r).
have es:is_equivalence s by rewrite /s; apply equivalence_preorder1.
have ss:substrate s = substrate r.
  by rewrite /s substrate_equivalence_associated_o.
have fep: is_function (ext_to_prod (canon_proj s) (canon_proj s)).
  by apply ext_to_prod_function; apply canon_proj_function=>//.
have gi:
  is_graph (image_by_fun (ext_to_prod (canon_proj s) (canon_proj s)) r).
  move=> t; rewrite /image_by_fun; aw; move=> [x [xr Jg]].
  by move:(inc_pr2graph_target fep Jg); rewrite /ext_to_prod; aw; intuition.
 rewrite graph_exten //; last by apply order_associated_graph.
move=> u v; rewrite order_associated_related2 //.
fold s; rewrite /related/image_by_fun; apply iff_eq.
  move=> [uq [vq hyp]].
  aw;exists (J (rep u) (rep v)).
  split; first by apply hyp; apply (inc_rep_itself es).
  have p1: (class s (rep u) =u) by apply class_rep.
  have p2: (class s (rep v) =v) by apply class_rep.
  have : (J u v = J (class s (rep u)) (class s (rep v))) by rewrite p1 p2.
  rewrite - ext_to_prod_rel_W; fprops; move =>->.
  apply W_pr3=> //.
  by rewrite /ext_to_prod; aw; fprops.
aw; move=> [x [xr]].
rewrite/related=> Jg; move: (W_pr fep Jg)=> Wx.
have px: (is_pair x) by move: pr=> [[g _] _]; apply g.
have Jpq: J (P x) (Q x) = x by aw.
have ps: (inc (P x) (substrate s)) by rewrite ss; substr_tac.
have qs: (inc (Q x) (substrate s)) by rewrite ss; substr_tac.
move: Wx; rewrite -Jpq ext_to_prod_rel_W // => JJ.
move: (pr1_def JJ) (pr2_def JJ) => e1 e2.
have uq: inc u (quotient s) by rewrite - e1; gprops.
have vq: inc v (quotient s) by rewrite - e2; gprops.
ee; move=> a b au bv.
suff: (related (order_associated r) u v).
  by rewrite order_associated_related2 //; move=> [_ [_ h]]; apply (h _ _ au bv).
rewrite order_associated_related1 //;ee.
exists (P x); exists (Q x).
rewrite -e1 -e2; split; first by apply inc_itself_class.
split; first by apply inc_itself_class.
by red; rewrite px.
Qed.

Notation and terminology


Same axioms as in Bourbaki
Definition order_axioms (r s:Set) :=
  (forall x y z, gle r x y -> gle r y z -> gle r x z) &
  (forall x y, gle r x y -> gle r y x -> x = y) &
  (forall x y, gle r x y -> (inc x s & inc y s)) &
  (forall x, gle r x x = inc x s) &
  is_graph r.

Lemma axioms_of_order: forall r,
  order r = (order_axioms r (substrate r)).
Proof.
move=> r; rewrite/order_axioms; apply iff_eq.
  move=>or; intuition; try order_tac.
  apply iff_eq=> H; order_tac.
move=> [h1 [h2 [h3 [h4 gr]]]].
hnf. intuition; split=>//.
by move=> y; rewrite -h4.
Qed.

Lemma le_pr: forall r x y,
  inc x (substrate r) ->
  order r -> gle r x y = (glt r x y \/ x = y).
Proof.
move=> r x y xr or; rewrite /glt.
apply iff_eq.
  case (equal_or_not x y); auto.
case=>//; first by intuition.
by move=> <-; order_tac.
Qed.

Order morphisms and isomorphisms. In the second definition we do not assume the function surjective

Definition order_isomorphism f r r':=
  (order r) & (order r') &
  (bijection f) & (substrate r = source f) & (substrate r' = target f) &
  (forall x y, inc x (source f) -> inc y (source f) ->
    gle r x y = gle r' (W x f) (W y f)).

Definition order_morphism f r r':=
  (order r) & (order r') &
  (injection f) & (substrate r = source f) & (substrate r' = target f) &
  (forall x y, inc x (source f) -> inc y (source f) ->
    gle r x y = gle r' (W x f) (W y f)).

Ordered subsets. Product of ordered sets


Order induced by r on a
Definition induced_order (r a:Set):=
  intersection2 r (coarse a).

Lemma relation_induced_order: forall r a,
  is_graph r -> is_graph (induced_order r a).
Proof.
rewrite /induced_order=> r a gr; apply (inter2_is_graph1 gr (y:=coarse a)).
Qed.

Lemma iorder_gle: forall r a x y, inc x a -> inc y a ->
  gle (induced_order r a) x y = gle r x y.
Proof.
rewrite /gle/related/induced_order=> r a x y xa ya.
apply iff_eq; aw; first by case.
move=> h; split=>//; rewrite /coarse; fprops.
Qed.

Lemma iorder_gle1: forall r a x y,
  gle (induced_order r a) x y -> gle r x y.
Proof.
move=> r a x y; rewrite/induced_order.
by rewrite /gle/related; aw; case.
Qed.

Lemma iorder_gle2: forall r a x y,
  glt (induced_order r a) x y -> glt r x y.
Proof.
move=> r a x y [lexy nxy]; split =>//.
apply (iorder_gle1 lexy).
Qed.

Lemma iorder_gle3: forall r a x y,
  gle (induced_order r a) x y -> (inc x a & inc y a).
Proof.
rewrite /induced_order/gle/related/coarse => r x a y.
by aw; intuition.
Qed.

Lemma iorder_gle4: forall r a x y,
  glt(induced_order r a) x y -> (inc x a & inc y a).
Proof.
move=> r a x y [glexy nexy]; apply (iorder_gle3 glexy).
Qed.

Hint Rewrite iorder_gle : aw.

Lemma iorder_gle5: forall r a x y,
  gle (induced_order r a) x y = (inc x a & inc y a & gle r x y).
Proof.
move=> r a x y; apply iff_eq.
  move=> h; move: (iorder_gle1 h)(iorder_gle3 h).
  intuition.
move=> [h1 [h2 h3]]; aw.
Qed.

Lemma iorder_gle6: forall r a x y,
  glt (induced_order r a) x y = (inc x a & inc y a & glt r x y).
Proof.
move=> r a x y; rewrite /glt iorder_gle5.
apply iff_eq; intuition.
Qed.

Lemma substrate_induced_order1: forall r a,
  is_reflexive r ->
  sub a (substrate r) -> substrate (induced_order r a) = a.
Proof.
move=> r a rr sasr.
have gi: is_graph (induced_order r a).
  by apply relation_induced_order; move: rr => [g].
rewrite /induced_order/coarse.
set_extens x.
  rewrite inc_substrate_rw //.
  by case; move=> [y]; aw; intuition.
move=> h.
apply inc_arg1_substrate with x.
red; aw; intuition.
move: (sasr _ h); rewrite reflexive_inc_substrate //.
Qed.

Lemma substrate_induced_order :forall r a,
  order r ->
  sub a (substrate r) -> substrate (induced_order r a) = a.
Proof.
move=> r a or ar; apply substrate_induced_order1=>//.
by move: or=> [].
Qed.

Hint Rewrite substrate_induced_order : aw.

Lemma reflexive_induced_order: forall r a,
  sub a (substrate r) ->
  is_reflexive r -> is_reflexive (induced_order r a).
Proof.
move=> r a ar [gr rr].
split; first by apply relation_induced_order.
move=> y ys.
have ya: (inc y a) by rewrite substrate_induced_order1 in ys.
have ysr: (inc y (substrate r)) by apply ar.
by rewrite -[related]/(gle); aw; apply rr.
Qed.

Lemma transitive_induced_order: forall r a,
  sub a (substrate r) ->
  is_transitive r -> is_transitive (induced_order r a).
Proof.
move=> r a ar [gr tr].
split; first by apply relation_induced_order.
move=> x y z; rewrite /related/induced_order/coarse; aw.
move => [J1r [_ [xa ya]]][J2r [_ [_ za]]].
intuition; apply (tr _ _ _ J1r J2r).
Qed.

Lemma preorder_induced_order: forall r a,
  sub a (substrate r) ->
  preorder r -> preorder (induced_order r a).
Proof.
move=> r a ar [rr tr].
by split; [ apply reflexive_induced_order | apply transitive_induced_order].
Qed.

Lemma order_induced_order: forall r a,
  sub a (substrate r) ->
  order r -> order (induced_order r a).
Proof.
move=> r a ar or.
have: preorder (induced_order r a).
  by apply preorder_induced_order =>//; apply order_preorder.
move=> [rio tio].
hnf; intuition.
split; first by move: rio => [g _].
move=> x y; rewrite /related /induced_order.
aw;move=> [J1 _][J2 _]; order_tac.
Qed.

Hint Resolve order_induced_order: fprops.

Lemma induced_order_substrate: forall r,
  order r ->
  induced_order r (substrate r) = r.
Proof.
move=> r o; rewrite /induced_order -intersection2_sub.
apply sub_graph_coarse_substrate; fprops.
Qed.

Lemma opposite_induced: forall r x, order r ->
  induced_order (opposite_order r) x = opposite_order (induced_order r x).
Proof.
move=> r x or; rewrite /induced_order/opposite_order/inverse_graph/coarse.
have gr: is_graph r by fprops.
move:(inter2_is_graph1 gr (y:=coarse x)) => gi.
set_extens y; rewrite Z_rw; aw; rewrite Z_rw.
  move=> [[yp Jr] [py [Px Qx]]].
  dw;intuition; [ exists(Q y) |exists (P y)];aw; fprops.
move=> [[py [ [u J1i][b J2i]]] [Jr [_ [Qx Qy]]]].
intuition; aw; intuition; ex_tac.
Qed.

Example 1. The function that associates to a partial function its graph. Is an isomorphism for extends and inclusion
Definition set_of_fgraphs (x y :Set):=
  (Zo(powerset (product x y)) (fun z => functional_graph z)).

Definition graph_of_function (x y:Set):=
  BL graph (set_of_sub_functions x y)
  (set_of_fgraphs x y).

Lemma graph_of_function_sub: forall x y z,
  inc z (set_of_sub_functions x y) -> sub (graph z) (product x y).
Proof.
move=> x y z; bw; move=> [fz [sz]] <-.
move: fz=> [[_ p] _].
apply (@sub_trans _ _ (product x (target z)) p).
apply product_monotone; fprops.
Qed.

Lemma set_of_graphs_pr: forall x y z,
  inc z (set_of_sub_functions x y) -> inc (graph z) (set_of_fgraphs x y).
Proof.
move=> x y z zs; rewrite /set_of_fgraphs Z_rw; split.
  by aw; apply graph_of_function_sub.
move: zs; bw; move => [[cz [fz _]] _].
by move: fz; rewrite - is_functional; case.
Qed.

Lemma graph_of_function_axioms: forall x y,
  transf_axioms(fun g => (graph g)) (set_of_sub_functions x y)
  (set_of_fgraphs x y).
Proof. by move=> x y z zs; apply set_of_graphs_pr.
Qed.

Lemma graph_of_function_fonction: forall x y,
  is_function (graph_of_function x y).
Proof.
move=> x y; rewrite/graph_of_function.
by apply bl_function; apply graph_of_function_axioms.
Qed.

Lemma graph_of_function_W: forall x y f,
  inc f (set_of_sub_functions x y) -> W f (graph_of_function x y) = graph f.
Proof.
by rewrite /graph_of_function=> x y f fs; aw; apply graph_of_function_axioms.
Qed.

Lemma graph_of_function_bijective: forall x y,
  bijection (graph_of_function x y).
Proof.
rewrite /graph_of_function=> x y.
apply bl_bijective.
    by apply graph_of_function_axioms.
  move=> u v; bw; move=> [fu [ssu tu]][fv [sv tv]] sg.
  by apply function_exten1=>//; ue.
move=> z; rewrite /set_of_fgraphs Z_rw; aw; move=> [zp fgz].
move: (sub_product_is_graph zp) => gz.
have fgz': fgraph z by rewrite - is_functional.
exists (corresp (domain z) y z); bw; aw; intuition.
  apply is_function_pr=>//.
  move => t; dw; move => [u Jz]; move: (zp _ Jz); aw; intuition.
  move => t; dw; move => [u Jz]; move: (zp _ Jz); aw; intuition.
Qed.

Lemma graph_of_function_isomorphism: forall x y,
  order_isomorphism (graph_of_function x y)
  (opposite_order (extension_order x y))
  (inclusion_suborder (set_of_fgraphs x y)).
Proof.
move=> x y.
move: (extension_is_order x y) => eo.
red; aw.
have <- :set_of_sub_functions x y = source (graph_of_function x y).
  rewrite /graph_of_function; aw.
ee.
    by apply graph_of_function_bijective.
  by rewrite /graph_of_function; aw.
move=> a b ass bss.
rewrite graph_of_function_W // graph_of_function_W // extension_order_pr2.
by aw; apply iff_eq; move=> [aas' [bss' sg]]; ee ; apply set_of_graphs_pr.
Qed.

Example 2. The mapping that associates to a partition the graph of the equivalence is an order isomorphism

Definition graph_of_partition x :=
  BL(fun y => partition_relation_set y x)
  (set_of_partition_set x)(powerset (coarse x)).

Lemma gop_axioms: forall x,
  transf_axioms (fun y => partition_relation_set y x)
     (set_of_partition_set x) (powerset (coarse x)).
Proof.
move=>x y; rewrite set_of_partition_rw; aw.
apply sub_partition_relation_set_coarse.
Qed.

Lemma gop_W: forall x y,
  partition y x ->
  W y (graph_of_partition x) = partition_relation_set y x.
Proof.
rewrite /graph_of_partition=> x y pa; aw.
  apply gop_axioms.
by rewrite set_of_partition_rw.
Qed.

Lemma gop_morphism: forall x,
  order_morphism (graph_of_partition x) (coarser x)
  (opposite_order (inclusion_order (coarse x))).
Proof.
move=> x.
have oi: (order (inclusion_order (coarse x))) by intuition.
have gi: (is_graph (inclusion_order (coarse x))) by fprops.
have sg: source (graph_of_partition x) = (set_of_partition_set x).
  by rewrite /graph_of_partition; aw.
have oc: order (coarser x) by apply coarser_order.
have ig: injection (graph_of_partition x).
   apply bl_injective; first by apply gop_axioms.
   move=> u v; rewrite 2! set_of_partition_rw => pu pv pp.
  apply (order_antisymmetry (coarser_order x) (a:=u) (b:=v)).
  rewrite - partition_relation_set_order // pp; fprops.
  rewrite - partition_relation_set_order // pp; fprops.
red; rewrite sg coarser_substrate; ee.
  rewrite /graph_of_partition; aw.
rewrite /gle=> a b.
do 2 rewrite set_of_partition_rw;move => pa pb.
rewrite - partition_relation_set_order // gop_W // gop_W //.
bw; aw; apply iff_eq; last by move=> [_ [_ h]].
by move=> p; ee; apply sub_partition_relation_set_coarse.
Qed.

Example 3: Compare preorder relations

Definition set_of_preorders x :=
  Zo (powerset (coarse x))(fun z => substrate z = x & preorder z).

Lemma set_of_preorders_rw: forall x z,
  inc z (set_of_preorders x) = (substrate z = x & preorder z).
Proof.
move=> x z; rewrite/set_of_preorders; apply iff_eq; rewrite Z_rw.
   intuition.
move=> [sz p]; intuition.
by aw; rewrite -sz; apply sub_graph_coarse_substrate; apply preorder_graph.
Qed.

Definition coarser_preorder x :=
  inclusion_suborder (set_of_preorders x).

Lemma coarser_preorder_order: forall x,
  order (coarser_preorder x).
Proof. rewrite /coarser_preorder=> x; apply subinclusion_is_order.
Qed.

Lemma coarser_preorder_substrate: forall x,
  substrate (coarser_preorder x) = set_of_preorders x.
Proof. by rewrite /coarser_preorder=> x; apply substrate_subinclusion_order.
Qed.

Lemma coarser_preorder_related: forall x u v,
  related (coarser_preorder x) u v =
  (preorder u & preorder v & substrate u = x & substrate v = x & sub u v).
Proof.
move=> x u v; rewrite /coarser_preorder subinclusion_order_rw.
rewrite 2!set_of_preorders_rw.
apply iff_eq; intuition.
Qed.

Lemma coarser_preorder_related1: forall x u v,
  related (coarser_preorder x) u v =
  (preorder u & preorder v & substrate u = x & substrate v = x &
  forall a b, inc a x -> inc b x -> related u a b -> related v a b).
Proof.
move=> x u v; rewrite coarser_preorder_related.
apply iff_eq; move=> [pu [pv [su [sv hyp]]]]; ee.
  by move=> a b ax bx; rewrite /related => J1; apply hyp.
move => t tu.
have gu:is_graph u by apply preorder_graph.
move: (gu _ tu) => p.
rewrite -p in tu |- *; apply hyp =>//; rewrite -su; substr_tac.
Qed.

If g is a family of orders, it induces an order of the product of f, the family of susbtrates of g

Definition fam_of_substrates g :=
  L (domain g) (fun i => substrate (V i g)).

Definition prod_of_substrates g := productb (fam_of_substrates g).

Definition product_order_r (g:Set): Set -> Set -> Prop :=
  fun x x' => forall i, inc i (domain g) -> gle (V i g) (V i x) (V i x').

Definition product_order g :=
  graph_on (product_order_r g)(prod_of_substrates g).

Definition product_order_axioms g:=
  fgraph g & (forall i, inc i (domain g) -> order (V i g)).

Lemma prod_of_substrates_rw: forall g x,
  product_order_axioms g ->
  (inc x (prod_of_substrates g) = (fgraph x & domain x = domain g &
    forall i, inc i (domain g) -> inc (V i x) (substrate (V i g)))).
Proof.
move=> g x [fg ao].
rewrite /prod_of_substrates/fam_of_substrates; aw; bw; gprops.
apply iff_eq; move => [fgx [dx asu]]; ee; move=> i idg.
  rewrite dx in asu; move: (asu _ idg); bw.
by rewrite dx in idg; bw; apply asu.
Qed.

Hint Rewrite prod_of_substrates_rw : aw.

Lemma product_order_order: forall g,
  product_order_axioms g -> order (product_order g).
Proof.
move=> g poa; move: (poa) => [fg oa].
have ffg: fgraph (fam_of_substrates g) by rewrite /fam_of_substrates; gprops.
rewrite /product_order/product_order_r.
apply order_from_rel1.
- move=> /= x y z pxy pyz i idg.
  move: (pxy _ idg) (pyz _ idg)(oa _ idg)=> *; order_tac.
- move=> x y; rewrite /prod_of_substrates => xp yp p1 p2.
  apply (productb_exten ffg xp yp).
  rewrite /fam_of_substrates; bw.
  move=> i idg; move: (p2 _ idg) (p1 _ idg)(oa _ idg)=> *; order_tac.
- move=> u up i idg; move: (oa _ idg) => oi.
  by order_tac; move: up; aw; intuition.
Qed.

Lemma product_order_related: forall g x x',
  product_order_axioms g ->
  gle (product_order g) x x' =
  (inc x (prod_of_substrates g) & inc x' (prod_of_substrates g) &
  forall i, inc i (domain g) -> gle (V i g) (V i x)(V i x')).
Proof.
move=> g x x' pa; rewrite /product_order/gle/related graph_on_rw0.
by apply iff_eq.
Qed.

Hint Rewrite product_order_related : bw.
Hint Resolve product_order_order: fprops.

Lemma product_order_substrate: forall g,
  product_order_axioms g -> substrate (product_order g) = prod_of_substrates g.
Proof.
move=> g pa; rewrite /product_order.
apply substrate_graph_on.
move: (pa)=> [fg oa] a ap i idg; move: (oa _ idg) => oi.
order_tac; move: ap; aw; intuition.
Qed.

Hint Rewrite product_order_substrate : aw.

Lemma product_order_def: forall g,
  let f := fam_of_substrates g in
  product_order_axioms g ->
  image_by_fun (prod_of_products_canon f f) (product_order g)= (productb g).
Proof.
move=> g f pa. move: (pa)=> [fg oa].
have dfdg: domain f = domain g by rewrite /f/fam_of_substrates; bw.
set (F:= corresp (domain f) (range f) f).
have ff: fgraph f by rewrite /f/fam_of_substrates; gprops.
have fF: is_function F by rewrite /F; apply is_function_pr;fprops.
have gF: graph F = f by rewrite /F; aw.
have fp: is_function (prod_of_products_canon f f).
  move: (popc_bijection fF fF (refl_equal (source F))); rewrite gF=> h; fct_tac.
move:(product_order_order pa) => poo.
have spgs: sub (product_order g) (source (prod_of_products_canon f f)).
  rewrite /prod_of_products_canon; aw.
  have gpo: is_graph (product_order g) by fprops.
  move: (sub_graph_coarse_substrate gpo).
  by rewrite (product_order_substrate pa).
set_extens x.
  aw; move=> [u [upo]].
  move: (spgs _ upo); rewrite {1} /prod_of_products_canon bl_source- gF => upob.
  rewrite popc_W //; move => <-.
  move: upob. rewrite gF product_inc_rw productb_rw // productb_rw //.
  move => [pu [[fgP [dPu alp]] [fgQ [dQu alq]]]].
  rewrite /prod_of_function dPu dfdg; bw.
  ee; move=> i idg; bw.
  have: (gle (product_order g) (P u) (Q u)) by red; red; rewrite pu.
  rewrite product_order_related //.
  by move=> [_ [_ h]]; move: (h _ idg).
move=> wp; aw.
set (x1 := L (domain g) (fun i => P (V i x))).
set (x2:= (L (domain g) (fun i => Q (V i x)))).
move: (wp); aw; move =>[fgx [dxdg idx]]; rewrite dxdg in idx.
have p1: inc x1 (prod_of_substrates g).
  rewrite/x1; aw; ee;bw.
  by move => i idg; bw; apply inc_pr1_substrate; apply idx.
have p2: inc x2 (prod_of_substrates g).
  rewrite/x2; aw; ee;bw.
  by move => i idg; bw; apply inc_pr2_substrate; apply idx.
have p3: forall i, inc i (domain g) -> is_pair (V i x).
   move=> i idg; apply (order_is_graph (oa _ idg) (idx _ idg)).
exists (J x1 x2) ; split.
  move: (product_order_related x1 x2 pa); rewrite /gle /related.
  move=> ->; ee.
  by move=> i idg; rewrite/x1/x2; bw;aw; rewrite (p3 _ idg); apply idx.
rewrite -gF popc_W //.
  aw; rewrite /prod_of_function.
  rewrite /x1 /x2; apply fgraph_exten=> //; gprops.
  symmetry; rewrite /x1; bw.
  by bw; move=> i idg; bw; aw; apply p3.
by rewrite gF; apply product_pair_inc.
Qed.

Order on graphs of functions x->y, induced by an order g on the target. This is a special case of product order.
Definition graph_order_r (x g:Set): Set -> Set -> Prop :=
  fun z z' => forall i, inc i x -> gle g (V i z) (V i z').

Definition graph_order x y g :=
  graph_on (graph_order_r x g) (set_of_gfunctions x y).

Lemma product_order_axioms_x:forall x g, order g ->
  product_order_axioms (cst_graph x g).
Proof.
rewrite /cst_graph=> x g og; split;gprops.
bw; move=> i id; bw.
Qed.

Lemma graph_order_r_pr: forall x g z z',
  graph_order_r x g z z' =
  product_order_r (cst_graph x g) z z'.
Proof.
move=>x g z z'.
have dc: (domain (cst_graph x g) = x) by rewrite /cst_graph; bw.
rewrite /product_order_r dc /cst_graph.
apply iff_eq; first by move=> go i ix; bw; apply go.
move=> h y yx; move: (h _ yx); bw.
Qed.

Lemma graph_order_pr1:forall x y g, substrate g = y ->
    (set_of_gfunctions x y) = prod_of_substrates (cst_graph x g).
Proof.
move=> x y g sg.
rewrite -cst_graph_pr /prod_of_substrates /fam_of_substrates /cst_graph.
symmetry.
bw; apply f_equal; apply L_exten1=>//; move=> v vs; bw.
Qed.

Lemma graph_order_pr:forall x y g, substrate g = y ->
  graph_order x y g = product_order (cst_graph x g).
Proof.
move=> x y g sg; rewrite/graph_order/product_order.
rewrite (graph_order_pr1 x sg).
rewrite {2}/cst_graph.
rewrite /graph_on/product_order_r L_domain.
set_extens t; rewrite !Z_rw; move=> [t1 h];split =>// i ix; move: (h _ ix); bw.
Qed.

Lemma graph_order_order: forall x y g, order g -> substrate g = y ->
  order (graph_order x y g).
Proof.
move=> x y g og sg; rewrite graph_order_pr =>//.
apply product_order_order.
by apply product_order_axioms_x.
Qed.

Lemma graph_order_substrate: forall x y g, order g -> substrate g = y ->
  substrate (graph_order x y g) = set_of_gfunctions x y.
Proof.
move=> x y g ogf sg; rewrite graph_order_pr=>//.
rewrite product_order_substrate.
  by rewrite (graph_order_pr1 x sg).
by apply product_order_axioms_x.
Qed.

Order on functions x->y, induced by an order r on the target
Definition function_order_r x y r f g :=
  is_function f & is_function g & source f = x & target f = y
  & source g =x & target g = y &
  forall i, inc i x -> gle r (W i f) (W i g).

Definition function_order x y r :=
  graph_on(fun u v => function_order_r x y r u v)
  (set_of_functions x y).

Lemma function_order_reflexive: forall x y r u,
  order r -> substrate r = y ->
  inc u (set_of_functions x y) -> gle (function_order x y r) u u.
Proof.
move=> x y r u or sr us.
red; red; rewrite /function_order /graph_on Z_rw.
split;fprops; aw.
move: us; rewrite set_of_functions_rw; move => [fu [su tu]].
red; ee. move=> i ix; order_tac.
rewrite sr -tu; apply inc_W_target =>//; ue.
Qed.

Lemma function_order_substrate: forall x y r,
  order r -> substrate r = y ->
  substrate (function_order x y r) = set_of_functions x y.
Proof.
move=> x y r or sr.
have gf: is_graph (function_order x y r).
  by rewrite /function_order; apply graph_on_graph.
set_extens t.
  rewrite inc_substrate_rw //.
  case; move=> [z]; rewrite/function_order/graph_on Z_rw product_pair_rw;
    intuition.
move=> ts.
have a: inc (J t t) (function_order x y r) by apply function_order_reflexive.
substr_tac.
Qed.

Lemma function_order_pr:forall x y r f f',
  order r -> substrate r = y ->
  gle (function_order x y r) f f' =
  (inc f (set_of_functions x y) &
    inc f' (set_of_functions x y) &
    forall i, inc i x -> gle r (W i f) (W i f')).
Proof.
move=> x y r f f' or sr.
rewrite /gle/related/function_order/graph_on/function_order_r.
apply iff_eq; aw; rewrite Z_rw; aw; intuition.
Qed.

Lemma function_order_order: forall x y r,
  order r -> substrate r = y -> order (function_order x y r).
Proof.
move=> x y r or sr.
have go: (is_graph (function_order x y r)).
   rewrite /function_order;apply graph_on_graph.
red; ee; split=>//.
- rewrite function_order_substrate //.
  move=> z zs; apply function_order_reflexive =>//.
- move=> a b c; rewrite -[related]/(gle); do 3 rewrite function_order_pr //.
  move=> [afs [bfs leab]][_ [cfs lebc]]; ee.
  move=> i id; move: (leab _ id) (lebc _ id) => lab lbc.
  order_tac.
- move=> a b; rewrite -[related]/(gle); do 2 rewrite function_order_pr //.
  move=> [afs [bfs leab]][_ [_ leba]].
  move: afs bfs; aw; move => [fa [sa ta]] [fb [sb tb]].
  apply function_exten=>//; try ue.
  rewrite sa; move=> i ix; move: (leab _ ix) (leba _ ix)=> lab lba.
  order_tac.
Qed.

The mapping that associates to a function its graph is an isomorphism for the two orders defined above

Lemma function_order_isomorphism: forall x y r,
  order r -> substrate r = y ->
  order_isomorphism (BL graph (set_of_functions x y)(set_of_gfunctions x y))
  (function_order x y r)(graph_order x y r).
Proof.
move=> x y r or sr; red; ee.
- apply function_order_order =>//.
- apply graph_order_order =>//.
- apply graph_bijective.
- rewrite function_order_substrate=>//; aw.
- rewrite graph_order_substrate=>//; aw.
- move: (graph_axioms (x:=x) (y:=y))=> ax;aw=> a b ass bss.
  rewrite function_order_pr // bl_W // bl_W //.
  rewrite /gle/related/ graph_order/graph_on/graph_order_r
      Z_rw pr1_pair pr2_pair.
  apply iff_eq; intuition.
Qed.

Definition order_isomorphic r r':=
  exists f, order_isomorphism f r r'.
Notation "x \Is y" := (order_isomorphic x y) (at level 50).

Lemma function_order_isomorphic: forall x y r,
  order r -> substrate r = y ->
  (function_order x y r) \Is (graph_order x y r).
Proof.
move=> x y r or sr.
exists (BL graph (set_of_functions x y)(set_of_gfunctions x y)).
by apply function_order_isomorphism.
Qed.

Special case of a product of 2 orders

Definition product2_order f g:=
  graph_on (fun x x' => gle f (P x) (P x') & gle g (Q x) (Q x'))
    (product (substrate f)(substrate g)).

Lemma product2_order_pr: forall f g x x',
  gle (product2_order f g) x x' =
  (inc x (product (substrate f)(substrate g)) &
    inc x' (product (substrate f)(substrate g)) &
    gle f (P x) (P x') & gle g (Q x) (Q x')).
Proof.
move=> f g x x'.
rewrite /product2_order/graph_on/gle/related Z_rw.
rewrite pr1_pair pr2_pair product_pair_rw.
apply iff_eq; intuition.
Qed.

Lemma product2_order_preorder_substrate: forall f g,
  preorder f -> preorder g -> substrate (product2_order f g) =
  product (substrate f) (substrate g).
Proof.
move=> f g [[_ rf] _] [[_ rg] _ ]; rewrite /product2_order.
apply substrate_graph_on.
move => a; aw; move=> [pa [Ps Qs]].
by split; [apply rf | apply rg].
Qed.

Lemma product2_order_substrate: forall f g,
  order f -> order g -> substrate (product2_order f g) =
  product (substrate f) (substrate g).
Proof.
move=> *.
apply product2_order_preorder_substrate.
  by apply order_preorder.
by apply order_preorder.
Qed.

Lemma product2_order_preorder: forall f g,
  preorder f -> preorder g -> preorder (product2_order f g).
Proof.
rewrite /product2_order => f g [[_ rf][_ tf]] [[_ rg][_ tg]].
apply preorder_from_rel.
split.
  move => x y z[pxy qxy][pyz qyz].
  split; [ apply (tf _ _ _ pxy pyz) | apply (tg _ _ _ qxy qyz)].
move=> x y [pxy qxy]; red in pxy; red in qxy.
split; split; try apply rf; try apply rg; substr_tac.
Qed.

Lemma product2_order_order: forall f g,
  order f -> order g -> order (product2_order f g).
Proof.
move=> f g orf og.
have [rr tr]: preorder(product2_order f g).
  by apply product2_order_preorder; apply order_preorder.
hnf; intuition.
split; first by move: rr => [gr _].
move=> x y; rewrite -[related]/gle 2! product2_order_pr.
move=> [xp [yp [g1 g2]]] [_ [_ [p3 p4]]].
move: xp yp; aw; move => [px _] [qx _].
apply pair_exten => //; order_tac.
Qed.

Increasing mappings


Definition increasing_fun f r r':=
  is_function f & order r & order r' & substrate r = source f
  & substrate r' = target f &
  forall x y, gle r x y -> gle r' (W x f) (W y f).

Definition decreasing_fun f r r':=
  is_function f & order r & order r' & substrate r = source f
  & substrate r' = target f &
  forall x y, gle r x y -> gge r' (W x f) (W y f).

Definition monotone_fun f r r':=
  increasing_fun f r r' \/ decreasing_fun f r r'.

Lemma opposite_gle: forall r x y, order r ->
  gle (opposite_order r) x y = gle r y x.
Proof. rewrite /gle/related=> r x y; aw. Qed.
Lemma opposite_gge: forall r x y, order r ->
  gge (opposite_order r) x y = gle r x y.
Proof. rewrite /gge/gle/related=> r x y; aw.
Qed.

Hint Rewrite opposite_gle opposite_gge: aw.

Monotonicity and opposite order
Lemma increasing_fun_reva : forall f r r',
   increasing_fun f r r' -> decreasing_fun f r (opposite_order r').
Proof.
rewrite /increasing_fun/decreasing_fun=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y xy; aw; apply rel.
Qed.

Lemma increasing_fun_revb : forall f r r',
   increasing_fun f r r' -> decreasing_fun f (opposite_order r) r'.
Proof.
rewrite /increasing_fun/decreasing_fun=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y ; aw => xy; apply rel.
Qed.

Lemma decreasing_fun_reva : forall f r r',
   decreasing_fun f r r' -> increasing_fun f r (opposite_order r').
Proof.
rewrite /increasing_fun/decreasing_fun=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y ; aw => xy; apply rel.
Qed.

Lemma decreasing_fun_revb : forall f r r',
   decreasing_fun f r r' -> increasing_fun f (opposite_order r) r'.
Proof.
rewrite /increasing_fun/decreasing_fun=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y ; aw => xy; apply rel.
Qed.

Lemma monotone_fun_reva : forall f r r',
   monotone_fun f r r' -> monotone_fun f r (opposite_order r').
Proof.
rewrite /monotone_fun=> f r r'; case.
  by right; apply increasing_fun_reva.
by left; apply decreasing_fun_reva.
Qed.

Lemma monotone_fun_revb : forall f r r',
   monotone_fun f r r' -> monotone_fun f (opposite_order r) r'.
Proof.
rewrite /monotone_fun=> f r r'; case.
  by right; apply increasing_fun_revb.
by left; apply decreasing_fun_revb.
Qed.

Strict monotone functions

Definition strict_increasing_fun f r r':=
  is_function f & order r & order r' & substrate r = source f
  & substrate r' = target f &
  forall x y, glt r x y -> glt r' (W x f) (W y f).

Definition strict_decreasing_fun f r r':=
  is_function f & order r & order r' & substrate r = source f
  & substrate r' = target f &
  forall x y, glt r x y -> ggt r' (W x f) (W y f).

Definition strict_monotone_fun f r r':=
  strict_increasing_fun f r r' \/ strict_decreasing_fun f r r'.

Lemma glt_inva: forall r x y,
  order r -> glt r x y = ggt (opposite_order r) x y.
Proof. rewrite /ggt/glt=> r x y or; aw.
Qed.

Lemma ggt_inva: forall r x y,
  order r -> ggt r x y = glt (opposite_order r) x y.
Proof. rewrite /ggt/glt=> r x y or; aw.
Qed.

Lemma ggt_invb: forall r x y,
  ggt r x y = glt r y x.
Proof. rewrite /ggt/glt/gge=> r x y; by apply iff_eq; intuition.
Qed.

Lemma strict_increasing_fun_reva : forall f r r',
  strict_increasing_fun f r r' -> strict_decreasing_fun f r (opposite_order r').
Proof.
rewrite/ strict_increasing_fun/strict_decreasing_fun.
move=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y; rewrite -glt_inva// ; apply rel.
Qed.

Lemma strict_increasing_fun_revb : forall f r r',
  strict_increasing_fun f r r' -> strict_decreasing_fun f (opposite_order r) r'.
Proof.
rewrite/ strict_increasing_fun/strict_decreasing_fun.
move=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y; rewrite -ggt_inva// ggt_invb// ggt_invb//; apply rel.
Qed.

Lemma strict_decreasing_fun_reva : forall f r r',
  strict_decreasing_fun f r r' -> strict_increasing_fun f r (opposite_order r').
Proof.
rewrite/ strict_increasing_fun/strict_decreasing_fun.
move=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
by move=> x y; rewrite -ggt_inva// ; apply rel.
Qed.

Lemma strict_decreasing_fun_revb : forall f r r',
  strict_decreasing_fun f r r' -> strict_increasing_fun f (opposite_order r) r'.
Proof.
rewrite/ strict_increasing_fun/strict_decreasing_fun.
move=> f r r' [ff [or [or' [sr [sr' rel]]]]].
ee; first by aw.
 move=> x y; rewrite -ggt_inva// ggt_invb//.
by move=> hyp; rewrite -ggt_invb//; apply rel.
Qed.

Lemma strict_monotone_fun_reva : forall f r r',
  strict_monotone_fun f r r' -> strict_monotone_fun f r (opposite_order r').
Proof.
rewrite /strict_monotone_fun=> f r r'; case.
  by right; apply strict_increasing_fun_reva.
by left; apply strict_decreasing_fun_reva.
Qed.

Lemma strict_monotone_fun_revb : forall f r r',
   strict_monotone_fun f r r' -> strict_monotone_fun f (opposite_order r) r'.
Proof.
rewrite /strict_monotone_fun=> f r r'; case.
  by right; apply strict_increasing_fun_revb.
by left; apply strict_decreasing_fun_revb.
Qed.

Example of monotone functions
Lemma constant_fun_increasing: forall f r r',
  order r -> order r' -> substrate r = source f ->
  substrate r' = target f -> is_constant_function f ->
  increasing_fun f r r'.
Proof.
move=> f r r' or or' sr sr' [ff cf].
red; ee.
move=> x y rxy.
have xs: (inc x (source f)) by rewrite -sr; order_tac.
have ys: (inc y (source f)) by rewrite -sr;order_tac.
rewrite (cf _ _ xs ys); order_tac.
ue.
Qed.

Lemma constant_fun_decreasing: forall f r r',
  order r -> order r' -> substrate r = source f ->
  substrate r' = target f -> is_constant_function f ->
  decreasing_fun f r r'.
Proof.
move=> f r r' or or' sr sr' [ff cf].
red; ee.
move=> x y rxy.
have xs: (inc x (source f)) by rewrite -sr; order_tac.
have ys: (inc y (source f)) by rewrite -sr;order_tac.
rewrite (cf _ _ xs ys); red;order_tac.
ue.
Qed.

Lemma identity_increasing_decreasing : forall x,
  let r := identity_g x in
    (increasing_fun (identity x) r r &decreasing_fun (identity x) r r).
Proof.
move=> x r.
move: (diagonal_order x)(diagonal_substrate x) => oi si.
move:(identity_function x)=> fi.
split; red;ee; aw.
move=> a b rab; srw; rewrite -si; order_tac.
move=> a b rab;red.
srw; try (rewrite -si; order_tac).
move: rab; rewrite /r /gle/related. aw.
move=> [ax] <-; intuition.
Qed.

Lemma complementary_decreasing: forall E,
  strict_decreasing_fun(BL (fun X => complement E X)(powerset E)(powerset E))
  (inclusion_order E) (inclusion_order E).
Proof.
move=> E.
set (r:=inclusion_order E).
have or: order r by rewrite /r; fprops.
have sr: substrate r = powerset E by rewrite /r; aw.
have ta: transf_axioms (fun X => complement E X)
    (powerset E) (powerset E).
   by move=> c cE; aw;apply sub_complement.
red; aw; ee.
  by apply bl_function.
move=> x y; rewrite /r /glt; move=> xy; awi xy; move: xy => [[xE [yE xy]] nxy].
rewrite /ggt/gge; aw.
ee; try apply sub_complement.
  by move=> t; srw; move=> [tE nty]; split=> //; dneg tx; apply xy.
dneg cc; move:(f_equal (complement E) cc); srw.
Qed.

Definition set_of_majorants1 x r:=
  Zo (substrate r)(fun y => gle r x y).

Lemma set_of_majorants1_pr: forall x y r,
  order r -> inc x (substrate r) -> inc y (substrate r) ->
  (gle r x y = sub (set_of_majorants1 y r) (set_of_majorants1 x r)).
Proof.
rewrite /set_of_majorants1 => x y r or xs ys.
apply iff_eq.
  move=> lexy t; rewrite 2! Z_rw; move => [ts yt]; ee; order_tac.
set s:= Zo _ _; move=> ss.
have yss: inc y s by rewrite /s Z_rw; ee; order_tac.
by move: (ss _ yss); rewrite Z_rw; case.
Qed.

Lemma set_of_majorants1_decreasing: forall r,
  order r ->
  strict_decreasing_fun(BL (fun x=> (set_of_majorants1 x r))
    (substrate r)(powerset (substrate r))) r (inclusion_order (substrate r)).
Proof.
move=> r or; red; bw; aw.
set E:= substrate r.
have oe: order (inclusion_order E) by fprops.
have si: substrate (inclusion_order E) = powerset E by aw.
have ta: transf_axioms (fun x => set_of_majorants1 x r) E (powerset E).
  move=> x xE; aw.
  by move=> t; rewrite /set_of_majorants1 Z_rw; case.
ee; first by apply bl_function.
rewrite /ggt/gge => x y ltxy.
have xE: inc x E by rewrite /E;order_tac.
have yE: inc y E by rewrite /E;order_tac.
move: ltxy=> [lexy nxy].
move:(lexy); rewrite set_of_majorants1_pr //; move=> sxy.
aw; ee.
  by move: (ta _ yE); aw.
  by move: (ta _ xE); aw.
move=> s_eq.
have yx: gle r y x by rewrite set_of_majorants1_pr // s_eq; fprops.
elim nxy;order_tac.
Qed.

Lemma strict_increasing_from_injective: forall f r r',
  injection f -> increasing_fun f r r' -> strict_increasing_fun f r r'.
Proof.
move=> f r r' [_ inf] [ff [or [or' [sr [sr' hyp]]]]].
red; ee; move=> x y [lexy neq].
split; first by apply hyp.
dneg Wxy; apply inf=>//; rewrite -sr; order_tac.
Qed.

Lemma strict_decreasing_from_injective: forall f r r',
  injection f ->decreasing_fun f r r' -> strict_decreasing_fun f r r'.
Proof.
move=> f r r' [_ inf] [ff [or [or' [sr [sr' hyp]]]]].
red; ee; move=> x y [lexy neq].
split; first by apply hyp.
dneg Wxy; apply inf=>//; rewrite -sr; order_tac.
Qed.

Lemma strict_monotone_from_injective: forall f r r',
  injection f -> monotone_fun f r r' -> strict_monotone_fun f r r'.
Proof.
move=> f r r' inf; case.
 by left; apply strict_increasing_from_injective.
by right; apply strict_decreasing_from_injective.
Qed.

Lemma order_isomorphism_increasing: forall f r r',
  order_isomorphism f r r' ->
  strict_increasing_fun f r r'.
Proof.
move=> f r r' [or [or' [[injf surjf] [sr [sr' hyp]]]]].
apply strict_increasing_from_injective => //.
red; ee.
  fct_tac.
rewrite -sr in hyp.
move=> x y xy; rewrite - hyp //; order_tac.
Qed.

Lemma order_morphism_increasing: forall f r r',
  order_morphism f r r' ->
  strict_increasing_fun f r r'.
Proof.
move=> f r r' [or [or' [injf [sr [sr' hyp]]]]].
apply strict_increasing_from_injective =>//.
red; ee.
  fct_tac.
rewrite -sr in hyp.
move=> x y xy; rewrite - hyp //; order_tac.
Qed.

Lemma order_isomorphism_pr: forall f r r',
  order r -> order r' ->
  bijection f -> substrate r = source f -> substrate r' = target f ->
  (order_isomorphism f r r' =
    (increasing_fun f r r' & increasing_fun (inverse_fun f) r' r)).
Proof.
move=> f r r' or or' bf sr sr'.
rewrite /order_isomorphism.
move: (bijective_inv_function bf)=> bif.
have sf: source f = target (inverse_fun f) by rewrite /inverse_fun; aw.
have tf:target f = source (inverse_fun f) by rewrite /inverse_fun; aw.
apply iff_eq.
  move=> [_ [_ [_ [_ [ _ hyp]]]]].
  split;red; ee; aw.
      fct_tac.
    by rewrite -sr in hyp; move=> x y xy; rewrite - hyp //; order_tac.
  move=>x y rxy.
  have xt: inc x (target f) by rewrite -sr'; order_tac.
  have yt: inc y (target f) by rewrite -sr'; order_tac.
  rewrite hyp; try (rewrite sf ; apply inc_W_target=>//; ue).
  rewrite - (W_inverse bf xt (refl_equal (W x (inverse_fun f)))).
  by rewrite - (W_inverse bf yt (refl_equal (W y (inverse_fun f)))).
move=> [inf inif]; ee.
move=> x y xsf ysf.
move: inf inif=> [_ [_ [_ [_ [_ h1]]]]] [_ [_ [_ [_ [_ h2]]]]].
apply iff_eq.
  move=> hy; by apply h1.
move=> hyp.
rewrite (W_inverse2 bf xsf (refl_equal (W x f))).
rewrite (W_inverse2 bf ysf (refl_equal (W y f))).
by apply h2.
Qed.

Theorem decreasing_composition: forall u v r r',
  decreasing_fun u r r' -> decreasing_fun v r' r ->
  (forall x, inc x (substrate r) -> gle r (W (W x u) v) x) ->
  (forall x', inc x' (substrate r') -> gle r' (W (W x' v) u) x') ->
  (u \co (v \co u) = u & v \co (u \co v) = v).
Proof.
move=> u v r r' [fu [or [or' [sr [sr' du]]]]] [fv [_ [_ [svr [svr' dv]]]]] h h'.
have cvu: v \coP u by red; hnf; ue.
have fvu: is_function (v \co u) by fct_tac.
have cuvu: u \coP (v \co u) by hnf; aw; ue.
have cuv: u \coP v by red; hnf; ue.
have fuv: is_function (u \co v) by fct_tac.
have cvuv: v \coP (u \co v) by hnf; aw; ue.
split.
  apply function_exten;aw; first by fct_tac.
  move=> x xs.
  rewrite -sr in xs; move:(h _ xs)=> rel1; move: (du _ _ rel1)=> rel2.
  have p: inc (W x u) (substrate r') by rewrite sr';apply inc_W_target=> //; ue.
  move: (h' _ p)=> rel3; aw; try ue.
  red in rel2; order_tac.
apply function_exten; aw; first by fct_tac.
move=> x xs.
rewrite -svr in xs; move:(h' _ xs)=> rel1; move: (dv _ _ rel1)=> rel2.
have p: inc (W x v) (substrate r) by rewrite svr';apply inc_W_target=> //; ue.
move: (h _ p)=> rel3; aw; try ue.
red in rel2; order_tac.
Qed.

Lemma order_isomorphism_opposite: forall g r r',
  order_isomorphism g r r' ->
  order_isomorphism g (opposite_order r) (opposite_order r').
Proof. rewrite /order_isomorphism=> g r r'.
intuition; aw. intuition.
Qed.

Maximal and minimal elements


Definition maximal_element r a:=
  inc a (substrate r) & forall x, gle r a x -> x = a.
Definition minimal_element r a:=
  inc a (substrate r) & forall x, gle r x a -> x = a.

Lemma maximal_element_opp: forall r a,
  order r -> maximal_element r a -> minimal_element (opposite_order r) a.
Proof.
move=> r a or [ar hyp].
split; first by aw.
move=> x; aw; apply hyp.
Qed.

Lemma minimal_element_opp: forall r a,
  order r -> minimal_element r a -> maximal_element (opposite_order r) a.
Proof.
move=> r a or [ar hyp].
split; first by aw.
move=> x; aw; apply hyp.
Qed.

Lemma maximal_opposite: forall r x,
  order r -> maximal_element (opposite_order r) x = minimal_element r x.
Proof.
move=> r x or.
have oo: (order (opposite_order r)) by fprops.
apply iff_eq.
  move=> hyp; move: (maximal_element_opp oo hyp).
  rewrite /opposite_order inverse_graph_involutive; fprops.
by apply minimal_element_opp.
Qed.

Lemma minimal_inclusion: forall E y,
  let F:= complement (powerset E) (singleton emptyset) in
  inc y F -> (minimal_element (inclusion_suborder F) y = is_singleton y).
Proof.
move=> E y F yF.
have Fp: forall x, inc x F = (sub x E & nonempty x).
  rewrite /F=>w; srw; aw.
  apply iff_eq; move => [wE h]; split=>//.
    case (emptyset_dichot w)=>//; contradiction.
  by move=> aux; rewrite aux in h; apply not_nonempty_empty.
apply iff_eq.
  rewrite /minimal_element; aw; rewrite Fp; move=>[[yE [z zy]] sp].
  have stF: inc (singleton z) F.
    rewrite Fp; split; first by apply sub_singleton; apply yE.
    by apply nonempty_singleton.
  have sty: singleton z = y by apply sp; aw; ee; apply sub_singleton.
  by exists z.
move=> sy.
split; first by aw.
move=> x; aw; move=> [xF [_ xy]].
apply extensionality=>//.
move: xF; rewrite Fp; move=> [xE [t tx]]; move: (xy _ tx)=> ty.
move: sy; rewrite is_singleton_rw; move => [_ hyp].
by move=> z zy; rewrite - (hyp _ _ ty zy).
Qed.

Maximal partial function for extension order are total functions
Lemma maximal_prolongation: forall E F x,
  nonempty F -> inc x (set_of_sub_functions E F) ->
  maximal_element (opposite_order (extension_order E F)) x = (source x = E).
Proof.
move=> E F x [t tF] xs; rewrite maximal_opposite; fprops.
apply iff_eq.
  move: xs; bw; move=>[fx [sx tx]] [_ h].
  apply extensionality=>// => y yE.
  ex_middle yns.
  have aux: gle (extension_order E F) (tack_on_f x y t) x.
    rewrite extension_order_pr1 //; bw; ee.
        rewrite /tack_on_f; aw.
        by move=> z zt; case (tack_on_or zt); [apply sx |move=>->].
      rewrite /tack_on_f -tx;aw.
      set_extens z => zs; last by fprops.
      by rewrite tack_on_rw in zs; case zs =>//; move=> ->; ue.
    by rewrite /tack_on_f; aw; fprops.
  by rewrite -(h _ aux); rewrite /tack_on_f; aw; intuition.
move=> sx.
red; aw; split=>//.
move=> y; rewrite extension_order_rw; move => [ys [_ et]].
move: xs ys; bw; move => [fx [_ tx]] [fy [sy ty]].
move: (source_extends et)=> sy'.
symmetry;apply function_exten=>//; try ue.
  apply extensionality=>//; ue.
move=> z zs; apply (W_extends et zs).
Qed.

Greatest element and least element


Definition greatest_element r a:=
  inc a (substrate r) & forall x, inc x (substrate r) -> gle r x a.
Definition least_element r a:=
  inc a (substrate r) & forall x, inc x (substrate r) -> gle r a x.

Lemma unique_greatest: forall a b r,
  order r -> greatest_element r a -> greatest_element r b -> a = b.
Proof.
move=> a b r or [asr h] [bsr bh].
apply (order_antisymmetry or (bh _ asr) (h _ bsr)).
Qed.

Lemma unique_least: forall a b r,
  order r -> least_element r a -> least_element r b -> a = b.
Proof.
move=> a b r or [asr h] [bsr bh].
apply (order_antisymmetry or (h _ bsr) (bh _ asr)).
Qed.

Definition the_least_element r := choose [eta least_element r].

Lemma the_least_element_pr: forall r,
  order r -> (exists u, least_element r u) ->
  least_element r (the_least_element r).
Proof.
by rewrite /the_least_element=> r or h; apply choose_pr.
Qed.

Definition the_greatest_element r:= choose [eta greatest_element r].

Lemma the_greatest_element_pr: forall r,
  order r -> (exists u, greatest_element r u) ->
  greatest_element r (the_greatest_element r).
Proof.
by rewrite /the_greatest_element=> r or h; apply choose_pr.
Qed.

Lemma the_least_element_pr2: forall r x, order r ->
  least_element r x -> the_least_element r = x.
Proof.
move=> r x or le.
have exu: (exists u, least_element r u) by exists x.
move: (the_least_element_pr or exu)=> le1.
apply (unique_least or le1 le).
Qed.

Lemma the_greatest_element_pr2: forall r x, order r ->
  greatest_element r x -> the_greatest_element r = x.
Proof.
move=> r x or le.
have exu: (exists u, greatest_element r u) by exists x.
move: (the_greatest_element_pr or exu)=> le1.
apply (unique_greatest or le1 le).
Qed.

Lemma greatest_induced: forall r s x, order r ->
  sub s (substrate r) -> inc x s ->
  greatest_element r x ->
  the_greatest_element r = the_greatest_element (induced_order r s).
Proof.
move=> r s x or ssr xs grx.
rewrite (the_greatest_element_pr2 or grx).
symmetry; apply the_greatest_element_pr2;fprops.
split; aw.
by move => y yxs; aw; move: grx=> [_ h]; apply h; apply ssr.
Qed.

Lemma least_induced: forall r s x, order r ->
  sub s (substrate r) -> inc x s ->
  least_element r x ->
  the_least_element r = the_least_element (induced_order r s).
Proof.
move=> r s x or ssr xs grx.
rewrite (the_least_element_pr2 or grx).
symmetry; apply the_least_element_pr2;fprops.
split; aw.
by move => y yxs; aw; move: grx=> [_ h]; apply h; apply ssr.
Qed.

Lemma least_not_greatest: forall r x , order r ->
  least_element r x -> greatest_element r x ->
  is_singleton (substrate r).
Proof.
move => r x or [xs lex][_ gex]; exists x.
apply is_singleton_pr; split=>//.
move=> z zs; move: (lex _ zs)(gex _ zs)=> xy yx.
order_tac.
Qed.

Lemma greatest_reverse: forall a r,
  order r -> greatest_element r a -> least_element (opposite_order r) a.
Proof.
move=> a r or [asr hy]; split;aw.
by move=> x xs; aw; apply hy.
Qed.

Lemma least_reverse: forall a r,
  order r -> least_element r a -> greatest_element (opposite_order r) a.
Proof.
move=> a r or [asr hy]; split;aw.
by move=> x xs; aw; apply hy.
Qed.

Lemma the_least_reverse: forall r, order r ->
  (exists a, greatest_element r a) ->
  the_least_element (opposite_order r) = the_greatest_element r.
Proof.
move=> r or hy.
move: (the_greatest_element_pr or hy) => gr.
move: (greatest_reverse or gr) => le.
by apply the_least_element_pr2; fprops.
Qed.

Lemma greatest_maximal: forall a r,
  order r -> greatest_element r a -> maximal_element r a.
Proof.
move=> a r or [asr h]; split=>//.
move => x ax;
have xsr: inc x (substrate r) by order_tac.
move: (h _ xsr)=> xa; order_tac.
Qed.

Lemma least_minimal: forall a r,
  order r -> least_element r a -> minimal_element r a.
Proof.
move=> a r or [asr h]; split=>//.
move => x ax;
have xsr: inc x (substrate r) by order_tac.
move: (h _ xsr)=> xa; order_tac.
Qed.

Lemma greatest_unique_maximal: forall a b r,
  greatest_element r a -> maximal_element r b -> a = b.
Proof.
by move=> a b r [ar ha][br hb]; apply hb; apply ha.
Qed.

Lemma least_unique_minimal: forall a b r,
  least_element r a -> minimal_element r b -> a = b.
Proof.
by move=> a b r [ar ha][br hb]; apply hb; apply ha.
Qed.

Lemma least_is_intersection: forall s a,
  least_element (inclusion_suborder s) a ->
  nonempty s -> a = intersection s.
Proof.
move=> s a []; aw =>asr h nes.
set_extens x => xs.
  apply intersection_inc =>//.
  by move => y ys; move: (h _ ys); aw; move=> [_ [_ ]]; apply.
by apply intersection_forall with s.
Qed.

Lemma greatest_is_union: forall s a,
  greatest_element (inclusion_suborder s) a -> a = union s.
Proof.
move=> s a []; aw=> asr h.
set_extens x => xs; first by union_tac.
move: (union_exists xs)=> [y [xy ys]].
by move: (h _ ys); aw; move=> [_ [_ ]]; apply.
Qed.

Lemma intersection_is_least: forall s,
  inc (intersection s) s ->
  least_element (inclusion_suborder s) (intersection s) .
Proof.
move=> s iis; split; aw.
move=> x xs; aw; ee.
by apply intersection_sub.
Qed.

Lemma union_is_greatest: forall s,
  inc (union s) s -> greatest_element (inclusion_suborder s) (union s).
Proof.
move=> s iis; split; aw.
move=> x xs; aw; ee.
by apply union_sub.
Qed.

Lemma emptyset_is_least: forall E,
  least_element (inclusion_order E) emptyset.
Proof.
move=> E;split; first by aw; apply emptyset_sub_any.
move=> x; aw => xE.
ee; apply emptyset_sub_any.
Qed.

Lemma wholeset_is_greatest: forall E,
  greatest_element (inclusion_order E) E.
Proof.
move=> E;split; first by aw.
move=> x; aw=> xE; ee.
Qed.

Definition empty_function_tg F := BL id emptyset F.

Lemma empty_function_tg_function: forall F,
  is_function (empty_function_tg F).
Proof.
move => F; apply bl_function => t; case; case.
Qed.

Lemma least_prolongation: forall E F,
  least_element (opposite_order (extension_order E F))
  (empty_function_tg F).
Proof.
move=> E F; red.
have <-: set_of_sub_functions E F =
    substrate (opposite_order (extension_order E F)) by aw; fprops.
have p1:inc (empty_function_tg F) (set_of_sub_functions E F).
  bw;rewrite /empty_function_tg; aw.
  ee; [ apply empty_function_tg_function | apply emptyset_sub_any].
split=>//.
move=> x xs; rewrite opposite_gle; fprops.
rewrite extension_order_pr1; ee.
rewrite /empty_function_tg. rewrite /BL;aw; rewrite trivial_fgraph.
apply emptyset_sub_any.
Qed.

Lemma greatest_prolongation: forall E F x,
  greatest_element (opposite_order (extension_order E F)) x ->
  nonempty E -> small_set F.
Proof.
move=> E F x ge [t tE].
case (emptyset_dichot F).
  by move=> -> u v uF vF; elim (emptyset_pr vF).
move=> neF.
set (r:= opposite_order (extension_order E F)).
have oe: order (extension_order E F) by fprops.
have or: order r by rewrite /r; fprops.
have alc: forall u (Ha:inc u F), x = constant_function E F u Ha.
  move=> u Ha.
  set (f:=constant_function E F u Ha).
  have sF: source f = E by rewrite /f/constant_function; aw.
  have fsf:inc f (set_of_sub_functions E F). bw.
    split; first by apply constant_function_fun.
    split; first by ue.
    by rewrite /f/constant_function; aw.
  rewrite - (maximal_prolongation neF fsf) in sF.
  apply (greatest_unique_maximal ge sF).
move=> u v uF vF.
move: (alc _ vF); rewrite (alc _ uF) => eq.
move: (f_equal [eta W t] eq).
do 2 rewrite constant_W //.
Qed.

Lemma least_equivalence: forall r,
  is_reflexive r -> sub (identity_g (substrate r)) r.
Proof.
move=> r [ge rr] t. rewrite inc_diagonal_rw.
by move=> [pt [Ps PQ]]; rewrite -pt -PQ; apply rr.
Qed.

Lemma order_transportation: forall f r,
  let r':= image_by_fun (ext_to_prod f f) r in
  bijection f -> order r -> substrate r = source f ->
  (order r' & substrate r' = target f).
Proof.
move=> f r r' bf or sr.
set (E:= source f).
have ff: is_function f by fct_tac.
have fe: is_function (ext_to_prod f f) by apply ext_to_prod_function.
have zr': (forall z,inc z r' = (exists x, exists y, inc x E & inc y E &
    inc (J x y) r & z = J (W x f)(W y f))).
  have gr: is_graph r by fprops.
  move => z; rewrite /r'; apply iff_eq.
    rewrite /image_by_fun; aw; move=> [x [xr Jg]].
    move: (W_pr fe Jg)=> Wz.
    have xJ: J (P x) (Q x) = x by aw; apply gr.
    rewrite -xJ in Wz.
    have: inc (P x) (substrate r) by substr_tac.
    have: inc (Q x) (substrate r) by substr_tac.
    rewrite sr=> Qs Ps.
    rewrite ext_to_prod_W // in Wz.
    by exists (P x); exists (Q x); ee; ue.
  move=> [x [y [xE [yE [Jr zJ]]]]].
  rewrite /image_by_fun; aw; ex_tac.
  have xs: inc x (source f) by rewrite -sr; substr_tac.
  have ys: inc y (source f) by rewrite -sr; substr_tac.
  have xys: (inc (J x y) (source (ext_to_prod f f))).
    by rewrite /ext_to_prod; aw; ee.
  have zW: z = W (J x y) (ext_to_prod f f) by rewrite ext_to_prod_W //.
  by rewrite zW; graph_tac.
have gr': is_graph r'.
  by move=> z; rewrite zr'; move=> [x [y [_ [_ [_]]]]] ->; fprops.
have sr': substrate r' = target f.
   set_extens t => tr.
   move: tr; rewrite /substrate; aw;dw.
   case; move=> [y]; rewrite zr'; move=>[u [v [uE [vE [Jr zJ]]]]].
       by rewrite (pr1_def zJ); apply inc_W_target.
     by rewrite (pr2_def zJ); apply inc_W_target.
  move: bf=> [_ sj]; move: (surjective_pr2 sj tr) => [x [xsf Wt]].
  have Jr: (inc (J t t) r').
    rewrite zr'; exists x; exists x; ee; [ order_tac; ue | ue].
  by substr_tac.
split=>//.
split.
  split=>//; rewrite sr'; move=> y yr; red; rewrite zr'.
  move: bf=> [_ sj]; move: (surjective_pr2 sj yr) => [x [xsf Wt]].
  by exists x; exists x; rewrite Wt; ee; order_tac; ue.
split.
  split=>//.
  move=> x y z; rewrite /related 3! zr'.
  move=> [a [b [aE [bE [J1r Jab]]]]] [c [d [cE [dE [J2r Jcd]]]]].
  exists a; exists d.
  have bc: (b = c).
    move: bf=> [[_ ij] _ ]; apply ij =>//.
    by rewrite - (pr2_def Jab) - (pr1_def Jcd).
  rewrite bc in J1r.
  rewrite - (pr1_def Jab)- (pr2_def Jcd); ee.
  by order_tac.
split=>//.
move=> x y; rewrite /related 2! zr'.
move=> [a [b [aE [bE [J1r Jab]]]]] [c [d [cE [dE [J2r Jcd]]]]].
have Wbc: W b f = W c f by rewrite - (pr2_def Jab) - (pr1_def Jcd).
have Wad: W a f = W d f by rewrite - (pr1_def Jab) - (pr2_def Jcd).
move: bf=> [[_ ij] _ ].
have bc: b = c by apply ij =>//.
have ad: a = d by apply ij =>//.
rewrite bc in J1r; rewrite ad in J1r.
have cd: c = d by order_tac.
by rewrite (pr1_def Jab) (pr1_def Jcd) cd ad.
Qed.

Any ordered set can be extended by adjoining a greatest element
Definition order_with_greatest r a :=
  union2 r (product (tack_on (substrate r) a) (singleton a)).

Lemma order_with_greatest_pr: forall r a,
  let r':=order_with_greatest r a in
    order r -> ~ (inc a (substrate r)) ->
    (order r' & substrate r' = tack_on (substrate r) a &
      r = intersection2 r' (coarse (substrate r)) & greatest_element r' a).
Proof.
simpl; rewrite /order_with_greatest.
move=> r a or nas.
set (E:=substrate r).
set r':= union2 r _.
have src:sub r (coarse E)
  by rewrite /E; apply sub_graph_coarse_substrate; fprops.
have gr': is_graph r' by rewrite /r'; apply union2_is_graph; fprops.
have sr': substrate r'=tack_on E a.
  set_extens x.
    rewrite /r'/substrate /E;aw; dw.
    case; move=>[y]; aw;case => hyp; intuition; left ;substr_tac.
  move=> xt.
  have Jg:inc (J x x) r'.
    move: xt; rewrite tack_on_rw /r'.
    case => hyp.
      apply union2_first; order_tac.
    by apply union2_second; rewrite hyp; aw;intuition.
  by substr_tac.
have or': order r'.
  split.
    split=>//.
    rewrite sr' /r'/related; move=> y; aw.
    case; first by move=> hyp; left; order_tac.
    by move=> ->; intuition.
  split.
    split=>//.
    move=> x y z; rewrite /related/r'; aw.
    case=> h.
       case => h'; first by left; order_tac.
       right;move: h'=> [_ [_ za]]; ee; left; rewrite /E; substr_tac.
    move: h=> [_ [h1 ya]].
    case => h'; first by elim nas; rewrite -ya; substr_tac.
    by intuition.
  split=>//.
  move=> x y; rewrite /related/r'; aw.
  case=> h.
    case => h'; first by order_tac.
    move: h'=> [_ [_ xa]]; elim nas; rewrite -xa; substr_tac.
  move: h=> [_ [_ ya]]; rewrite ya.
  case => h'; first by elim nas; substr_tac.
  intuition.
ee.
  rewrite /r'; set_extens x => xr.
    by apply intersection2_inc; [aw; auto | apply src].
  move: xr; aw; move=> [p1 p2];case p1 =>//.
  move: p2; rewrite /coarse; aw.
  by move=> [_ [_ Qa]][_ [_ Qb]]; elim nas; by rewrite -Qb.
red; rewrite sr'.
split; fprops.
move=> x; rewrite /r'; red; red; aw; intuition.
Qed.

Theorem adjoin_greatest: forall r a E,
  order r -> substrate r = E -> ~ (inc a E) ->
  exists_unique (fun r' => order r' & substrate r' = tack_on E a &
    r = intersection2 r' (coarse E) & greatest_element r' a).
Proof.
move=> r a E or sr naE.
set(E':=tack_on E a).
set(r':= union2 r (product E' (singleton a))).
split.
  exists (order_with_greatest r a).
  rewrite -sr in naE; move: (order_with_greatest_pr or naE).
  by rewrite sr.
pose p x := (order x &
    substrate x = E' & r = intersection2 x (coarse E) & greatest_element x a).
suff : forall x y, p x -> p y -> sub x y.
  move=> hyp x y px py.
  apply extensionality; [ apply (hyp _ _ px py) | apply (hyp _ _ py px)].
move=> x y [ox [sx [ix gx]]] [oy [sy [iy gy]]] z zx.
have pz: is_pair z.
  have grx: is_graph x by fprops.
  by apply grx.
have PzE: inc (P z) E' by rewrite -sx; substr_tac.
have QzE: inc (Q z) E' by rewrite -sx; substr_tac.
move: PzE QzE; rewrite /E'; aw; move=> PzE QzE.
case PzE=> Pz.
  case QzE=> Qz.
    have: (inc z r) by rewrite ix /coarse;apply intersection2_inc; aw; fprops.
    by rewrite iy; aw; case.
  move: gy=>[ay ga].
  rewrite -pz Qz; apply ga; rewrite sy; rewrite/ E'; aw.
case QzE => Qz.
  have rel2 : gle x (Q z) (P z).
    move: gx=>[ax ga].
    have qe: (inc (Q z) E') by rewrite /E'; fprops.
    by rewrite -sx in qe; move: (ga _ qe); ue.
  have rel1 : gle x (P z) (Q z) by red; red; rewrite pz .
  have PQ: P z = Q z by order_tac.
  by elim naE; rewrite -Pz PQ.
rewrite -pz Qz.
by move: gy=>[ax ga]; apply ga; ue.
Qed.

A set A is cofinal if every element of E is bounded by an element of A
Definition cofinal_set r a :=
  sub a (substrate r) &
  (forall x, inc x (substrate r) -> exists y, inc y a & gle r x y).

Definition coinitial_set r a :=
  sub a (substrate r) &
  (forall x, inc x (substrate r) -> exists y, inc y a & gle r y x).

Lemma exists_greatest_cofinal: forall r,
  (exists x, greatest_element r x) =
  (exists a, cofinal_set r a & is_singleton a).
Proof.
move=> r; apply iff_eq.
  move=> [a [asr h]]; exists (singleton a).
  split.
    split; first by move=> t; aw; move=>->.
    by move=> x xr; exists a; aw; split=>//; apply h.
  by red; exists a.
move=> [a [[asr h] [b bp]]].
exists b;split.
  by apply asr; rewrite bp; aw.
move=> x xsr; move:(h _ xsr)=> [y []]; rewrite bp; aw.
by move=>->.
Qed.

Lemma exists_least_coinitial: forall r,
  (exists x, least_element r x) =
  (exists a, coinitial_set r a & is_singleton a).
Proof.
move=> r; apply iff_eq.
  move=> [a [asr h]]; exists (singleton a).
  split.
    split; first by move=> t; aw; move=>->.
    by move=> x xr; exists a; aw; split=>//; apply h.
  by red; exists a.
move=> [a [[asr h] [b bp]]].
exists b;split.
  by apply asr; rewrite bp; aw.
move=> x xsr; move:(h _ xsr)=> [y []]; rewrite bp; aw.
by move=>->.
Qed.

Upper and lower bounds

Definition upper_bound r X x :=
  inc x (substrate r) & forall y, inc y X -> gle r y x.
Definition lower_bound r X x :=
  inc x (substrate r) & forall y, inc y X -> gle r x y.

Lemma opposite_upper_bound: forall x X r, order r ->
  upper_bound r X x = lower_bound (opposite_order r) X x.
Proof.
rewrite /upper_bound/lower_bound=> x R r or; aw.
apply iff_eq; move=> [xsr h]; split=>//; move=> y yR; move: (h _ yR); aw.
Qed.

Lemma opposite_lower_bound: forall x X r, order r ->
  lower_bound r X x = upper_bound (opposite_order r) X x.
Proof.
rewrite /upper_bound/lower_bound=> x R r or; aw.
apply iff_eq; move=> [xsr h]; split=>//; move=> y yR; move: (h _ yR); aw.
Qed.

Lemma smaller_lower_bound: forall x y X r, preorder r ->
  lower_bound r X x -> gle r y x -> lower_bound r X y.
Proof.
rewrite /lower_bound=> x y X r [rr [_ tr]] [xH h] yx.
split. red in yx;substr_tac.
move=> z zX; apply (tr _ _ _ yx (h _ zX)).
Qed.

Lemma greater_upper_bound: forall x y X r, preorder r ->
  upper_bound r X x -> gle r x y -> upper_bound r X y.
Proof.
rewrite /lower_bound=> x y X r [rr [_ tr]] [xH h] yx.
split. red in yx;substr_tac.
move=> z zX; apply (tr _ _ _ (h _ zX) yx).
Qed.

Lemma sub_lower_bound: forall x X Y r,
  lower_bound r X x -> sub Y X-> lower_bound r Y x.
Proof.
rewrite /lower_bound=>x X Y r [xR lo] YX .
split=>//; move=> y yY; apply (lo _ (YX _ yY)).
Qed.

Lemma sub_upper_bound: forall x X Y r,
  upper_bound r X x -> sub Y X -> upper_bound r Y x.
Proof.
rewrite /lower_bound=>x X Y r [xR lo] YX .
split=>//; move=> y yY; apply (lo _ (YX _ yY)).
Qed.

Lemma least_element_pr: forall X r, order r -> sub X (substrate r) ->
  (exists a, least_element (induced_order r X) a) =
  (exists x, lower_bound r X x & inc x X).
Proof.
rewrite /lower_bound/least_element=> X r or Xr.
apply iff_eq.
  move=> [a []]; aw=> aX h; exists a; ee.
  by move=> y yX; move: (h _ yX); aw.
by move=> [x [[xsr hyp] xX]]; exists x; aw; ee; move=> y yX; aw; apply hyp.
Qed.

Lemma greatest_element_pr: forall X r, order r -> sub X (substrate r) ->
  (exists a, greatest_element (induced_order r X) a) =
  (exists x, upper_bound r X x & inc x X).
Proof.
rewrite /upper_bound/greatest_element=> X r or Xr.
apply iff_eq.
  move=> [a []]; aw=> aX h; exists a; ee.
  by move=> y yX; move: (h _ yX); aw.
by move=> [x [[xsr hyp] xX]]; exists x; aw; ee; move=> y yX; aw; apply hyp.
Qed.

A set can be bounded above, below, or both

Definition bounded_above r X := exists x, upper_bound r X x.
Definition bounded_below r X := exists x, lower_bound r X x.
Definition bounded_both r X := bounded_above r X & bounded_below r X.

Lemma bounded_above_sub: forall X Y r,
  sub Y X -> bounded_above r X -> bounded_above r Y.
Proof.
move=> X Y r YX [x ux]; exists x; apply (sub_upper_bound ux YX).
Qed.

Lemma bounded_below_sub: forall X Y r,
  sub Y X -> bounded_below r X -> bounded_below r Y.
Proof.
move=> X Y r YX [x ux]; exists x; apply (sub_lower_bound ux YX).
Qed.

Lemma bounded_both_sub: forall X Y r,
  sub Y X ->bounded_both r X -> bounded_both r Y.
Proof.
rewrite /bounded_both=> X Y r YX [p1 p2].
split; [ apply (bounded_above_sub YX p1) | apply (bounded_below_sub YX p2)].
Qed.

Lemma singleton_bounded:forall x r,
  is_singleton x -> order r -> sub x (substrate r) -> bounded_both r x.
Proof.
move=> x r [y yp] or xsr.
have ysr: inc y (substrate r) by apply xsr;rewrite yp; aw.
have yy: gle r y y by order_tac.
by split; exists y; split=>//; rewrite yp; move=> z; aw => ->.
Qed.

Least upper bound and greatest lower bound


Definition greatest_lower_bound r X x :=
  greatest_element (induced_order r (Zo (substrate r) (lower_bound r X))) x.
Definition least_upper_bound r X x :=
  least_element (induced_order r (Zo (substrate r) (upper_bound r X))) x.

Lemma greatest_lower_bound_pr: forall r X x,
  order r -> sub X (substrate r) ->
  greatest_lower_bound r X x = (lower_bound r X x
    & forall z, lower_bound r X z -> gle r z x).
Proof.
rewrite /greatest_lower_bound/greatest_element=> r X x or Xr.
set T:= Zo _ _.
have sT: sub T (substrate r) by rewrite /T; apply Z_sub.
aw.
apply iff_eq.
  move=> [xT hyp]; move: (xT); rewrite /T Z_rw; move=> [xsr lb]; split=>//.
  move=> z lbz.
  have zT: inc z T by rewrite /T Z_rw; split=>//; move: lbz=> [].
  by move: (hyp _ zT); aw.
move=> [lbx hyp].
have xT: inc x T by rewrite /T Z_rw; split=>//; move: lbx=> [].
split=>//; move=> y yT; aw; apply hyp.
move: yT; rewrite /T Z_rw; move=> [_ ]; apply.
Qed.

Lemma least_upper_bound_pr: forall r X x,
  order r -> sub X (substrate r) ->
  least_upper_bound r X x = (upper_bound r X x
    & forall z, upper_bound r X z -> gle r x z).
Proof.
rewrite /least_upper_bound/least_element=> r X x or Xr.
set T:= Zo _ _.
have sT: sub T (substrate r) by rewrite /T; apply Z_sub.
aw.
apply iff_eq.
  move=> [xT hyp]; move: (xT); rewrite /T Z_rw; move=> [xsr lb]; split=>//.
  move=> z lbz.
  have zT: inc z T by rewrite /T Z_rw; split=>//; move: lbz=> [].
  by move: (hyp _ zT); aw.
move=> [lbx hyp].
have xT: inc x T by rewrite /T Z_rw; split=>//; move: lbx=> [].
split=>//; move=> y yT; aw; apply hyp.
move: yT; rewrite /T Z_rw; move=> [_ ]; apply.
Qed.

Hint Rewrite greatest_lower_bound_pr least_upper_bound_pr: aw.

Lemma supremum_unique: forall x y X r, order r ->
  least_upper_bound r X x -> least_upper_bound r X y -> x = y.
Proof.
rewrite /least_upper_bound => x y X r or.
set oi := induced_order r _.
have ooi: order oi by rewrite /oi; apply order_induced_order =>// ; apply Z_sub.
apply (unique_least ooi).
Qed.

Lemma infimum_unique: forall x y X r, order r ->
  greatest_lower_bound r X x -> greatest_lower_bound r X y -> x = y.
Proof.
rewrite /greatest_lower_bound => x y X r or.
set oi := induced_order r _.
have ooi: order oi by rewrite /oi; apply order_induced_order =>// ; apply Z_sub.
apply (unique_greatest ooi).
Qed.

Definition of the supremum or infimum or a set or a pair

Definition supremum r X := choose (least_upper_bound r X).
Definition infimum r X := choose (greatest_lower_bound r X).
Definition sup r x y := supremum r (doubleton x y).
Definition inf r x y := infimum r (doubleton x y).
Definition has_supremum r X := (exists x, least_upper_bound r X x).
Definition has_infimum r X := (exists x, greatest_lower_bound r X x).

Lemma supremum_pr1: forall X r,
  has_supremum r X ->
  least_upper_bound r X (supremum r X).
Proof. rewrite /supremum=> *; apply choose_pr =>//. Qed.

Lemma infimum_pr1: forall X r,
  has_infimum r X ->
  greatest_lower_bound r X (infimum r X).
Proof. rewrite /infimum=> *; apply choose_pr =>//. Qed.

Hint Resolve supremum_pr1 infimum_pr1: fprops.

Lemma supremum_pr2: forall r X a, order r ->
  least_upper_bound r X a -> a = supremum r X.
Proof.
move=> r X a or le.
have hs: has_supremum r X by exists a.
move: (supremum_pr1 hs).
apply (supremum_unique or le).
Qed.

Lemma infimum_pr2: forall r X a, order r ->
  greatest_lower_bound r X a -> a = infimum r X.
Proof.
move=> r X a or le.
have hs: has_infimum r X by exists a.
move: (infimum_pr1 hs).
apply (infimum_unique or le).
Qed.

Lemma inc_supremum_substrate : forall X r,
  order r -> sub X (substrate r) -> has_supremum r X ->
  inc (supremum r X) (substrate r).
Proof.
move=> X r or Xsr hs.
move: (supremum_pr1 hs); aw; by move=> [[aa _] _].
Qed.

Lemma inc_infimum_substrate : forall X r,
  order r -> sub X (substrate r) -> has_infimum r X ->
  inc (infimum r X) (substrate r).
Proof.
move=> X r or Xsr hs.
move: (infimum_pr1 hs); aw; by move=> [[aa _] _].
Qed.

Lemma supremum_pr: forall X r,
  order r -> sub X (substrate r) -> has_supremum r X ->
  (upper_bound r X (supremum r X) &
    forall z, upper_bound r X z -> gle r (supremum r X) z).
Proof. move=> X r or Xsr hs; move: (supremum_pr1 hs); aw. Qed.

Lemma infimum_pr: forall X r,
  order r -> sub X (substrate r) -> has_infimum r X ->
  (lower_bound r X (infimum r X) &
    forall z, lower_bound r X z -> gle r z (infimum r X)).
Proof. move=> X r or Xsr hs; move: (infimum_pr1 hs); aw. Qed.

Properties of sup and inf of pairs

Lemma sup_pr: forall a b r,
  order r -> inc a (substrate r) -> inc b (substrate r)
  -> has_supremum r (doubleton a b) ->
  (gle r a (sup r a b) & gle r b (sup r a b) &
    forall z, gle r a z -> gle r b z -> gle r (sup r a b) z).
Proof.
move=> a b r or ast bsr hs.
have sd: sub (doubleton a b) (substrate r) by apply sub_doubleton.
move: (supremum_pr or sd hs) => [[sr h1] h2].
ee.
move=> z az bz; apply h2.
split; first by order_tac.
by move=>y; aw;case; move=> ->.
Qed.

Lemma inf_pr: forall a b r,
  order r -> inc a (substrate r) -> inc b (substrate r)
  -> has_infimum r (doubleton a b) ->
  (gle r (inf r a b) a & gle r (inf r a b) b&
    forall z, gle r z a -> gle r z b -> gle r z (inf r a b)).
Proof.
move=> a b r or ast bsr hs.
have sd: sub (doubleton a b) (substrate r) by apply sub_doubleton.
move: (infimum_pr or sd hs) => [[sr h1] h2].
ee.
move=> z az bz; apply h2.
split; first by order_tac.
by move=>y; rewrite doubleton_rw; case; move=> ->.
Qed.

Lemma least_upper_bound_doubleton: forall r x y z,
  order r -> gle r x z -> gle r y z ->
  (forall t, gle r x t -> gle r y t -> gle r z t) ->
  least_upper_bound r (doubleton x y) z.
Proof.
move=> r x y z or xz yz h.
move:(inc_arg1_substrate xz) => xsr.
move:(inc_arg1_substrate yz) => ysr.
aw.
  split.
    split; first by order_tac.
    by move=>t; rewrite doubleton_rw; case; move=> ->.
  move=> t tb; apply h; apply tb; fprops.
by move=>t; rewrite doubleton_rw; case; move=> ->.
Qed.

Lemma greatest_lower_bound_doubleton: forall r x y z,
  order r -> gle r z x -> gle r z y ->
  (forall t, gle r t x -> gle r t y -> gle r t z) ->
  greatest_lower_bound r (doubleton x y) z.
Proof.
move=> r x y z or xz yz h.
move:(inc_arg2_substrate xz) => xsr.
move:(inc_arg2_substrate yz) => ysr.
aw.
  split.
    split; first by order_tac.
    by move=>t; rewrite doubleton_rw; case; move=> ->.
  move=> t tb; apply h; apply tb; fprops.
by move=>t; rewrite doubleton_rw; case; move=> ->.
Qed.

Link between supremum and greatest element

Lemma greatest_is_sup: forall r X a,
  order r -> sub X (substrate r) ->
  greatest_element (induced_order r X) a -> least_upper_bound r X a.
Proof.
move=>r X a or Xsr []; aw => aX h.
split.
  split; first by apply Xsr.
  by move=> y yX; move: (h _ yX); aw.
by move=> z [zs h2]; apply (h2 _ aX).
Qed.

Lemma least_is_inf: forall r X a,
  order r -> sub X (substrate r) ->
  least_element (induced_order r X) a -> greatest_lower_bound r X a.
Proof.
move=>r X a or Xsr []; aw => aX h.
split.
  split; first by apply Xsr.
  by move=> y yX; move: (h _ yX); aw.
by move=> z [zs h2]; apply (h2 _ aX).
Qed.

Lemma inf_sup_opp: forall r X a,
  order r -> sub X (substrate r) ->
  greatest_lower_bound r X a = least_upper_bound (opposite_order r) X a.
Proof.
move=> e X a or Xsr; aw; fprops.
rewrite -opposite_lower_bound //.
apply iff_eq; move=> [lb albe];
 split =>//; move => z; move: (albe z); rewrite -opposite_lower_bound //; aw.
Qed.

Lemma sup_inf_opp: forall r X a,
  order r -> sub X (substrate r) ->
  least_upper_bound r X a = greatest_lower_bound (opposite_order r) X a.
Proof.
move=> r X a or Xsr; rewrite inf_sup_opp; fprops; try (solve [aw]).
have ->: (opposite_order (opposite_order r) = r) =>//.
  by rewrite /opposite_order; apply inverse_graph_involutive; fprops.
Qed.

Supremum and infimum of the empty set
Lemma set_of_lower_bounds_emptyset: forall r,
  Zo (substrate r) (lower_bound r emptyset) = substrate r.
Proof.
move=> r;apply extensionality; first by apply Z_sub.
move=> x xsr; rewrite Z_rw; split=>//; split=>//.
move=> y;case; case.
Qed.

Lemma set_of_upper_bounds_emptyset: forall r ,
  Zo (substrate r) (upper_bound r emptyset) = substrate r.
Proof.
move=> r;apply extensionality; first by apply Z_sub.
move=> x xsr; rewrite Z_rw; split=>//; split=>//.
move=> y;case; case.
Qed.

Lemma least_upper_bound_emptyset: forall r x, order r ->
  least_upper_bound r emptyset x = least_element r x.
Proof.
move=> r x or; rewrite /least_upper_bound set_of_upper_bounds_emptyset.
by rewrite induced_order_substrate.
Qed.

Lemma greatest_lower_bound_emptyset: forall r x, order r ->
  greatest_lower_bound r emptyset x = greatest_element r x.
Proof.
move=> r x or; rewrite /greatest_lower_bound set_of_lower_bounds_emptyset.
by rewrite induced_order_substrate.
Qed.

Supremum and infimum for inclusion order


Lemma intersection_is_inf: forall s E, sub s (powerset E) ->
  greatest_lower_bound (inclusion_order E) s
  (Yo (nonempty s) (intersection s) E).
move=> s E sp.
have oi: order (inclusion_order E) by fprops.
have nnee: ~ nonempty emptyset by move=>h; apply not_nonempty_empty.
case (emptyset_dichot s)=> h.
  rewrite h Y_if_not_rw // greatest_lower_bound_emptyset //.
  by apply wholeset_is_greatest.
Ytac0.
have si: sub (intersection s) E.
  move: h=> [y ys]; apply sub_trans with y.
    by apply intersection_sub.
   move: (sp _ ys); aw.
aw; split.
  split; first by aw.
  move=> y ys; aw; split=>//.
  split; [ move:(sp _ ys); aw | apply (intersection_sub ys)].
move=> z []; aw=> zE hyp.
ee.
move=> t tz; apply intersection_inc =>//.
move=> y ys; move: (hyp _ ys); aw.
by move=> [_[_]]; apply.
Qed.

Lemma union_is_sup: forall s E, sub s (powerset E) ->
  least_upper_bound (inclusion_order E) s (union s).
Proof.
move=> s E sp.
have oi: order (inclusion_order E) by apply inclusion_is_order.
have su:sub (union s) E.
  by apply sub_union; move=> y ys; move: (sp _ ys); aw.
aw; rewrite /upper_bound; aw.
split.
  split=>// y ys.
  have sy: sub y (union s) by apply union_sub.
   aw; ee; move: (sp _ ys); aw.
move=> z []; aw; move=> zE hyp; ee.
apply sub_union => y ys; by move: (hyp _ ys); aw; move=> [_[_]]; apply.
Qed.

Lemma union_is_sup1: forall s F E, sub F (powerset E) ->
  sub s F -> inc (union s) F ->
  least_upper_bound (inclusion_suborder F) s (union s).
Proof.
move=> s F E Fp sF uF.
have oi: order (inclusion_suborder F) by apply subinclusion_is_order.
have sss: sub s (substrate (inclusion_suborder F)).
  by rewrite substrate_subinclusion_order.
rewrite least_upper_bound_pr //.
split.
  by red; aw; ee; move=> y ys; aw; ee; apply union_sub.
move=> z []; aw => zF hyp; ee.
apply sub_union => y ys; by move: (hyp _ ys); aw; move=> [_[_]]; apply.
Qed.

Lemma intersection_is_inf1: forall s F E, sub F (powerset E) ->
  sub s F -> inc (Yo (nonempty s) (intersection s) E) F ->
  greatest_lower_bound (inclusion_suborder F) s
  (Yo (nonempty s) (intersection s) E).
Proof.
move=> s F E Fp sF ssF.
have oi: order (inclusion_suborder F) by apply subinclusion_is_order.
have sss: sub s (substrate (inclusion_suborder F)).
  by rewrite substrate_subinclusion_order.
rewrite greatest_lower_bound_pr //.
split.
  red; aw; split =>//.
  move=> y ys; aw; ee.
  have nes: (nonempty s) by exists y.
  Ytac0;apply intersection_sub=>//.
move=>z []; aw => zF hyp;ee.
case (emptyset_dichot s) => sp.
  rewrite Y_if_not_rw.
    by apply powerset_sub; apply Fp.
  by rewrite sp=> h; apply not_nonempty_empty.
Ytac0;move=> t tz; apply intersection_inc=>//.
by move=> y ys; move: (hyp _ ys); aw; move=> [_ [_]]; apply.
Qed.

Supremum for extension order: is least common extension

Lemma sup_extension_order1: forall E F T f,
  sub T (set_of_sub_functions E F) ->
  least_upper_bound (opposite_order (extension_order E F)) T f ->
  forall u v x, inc u T -> inc v T -> inc x (source u) -> inc x (source v) ->
    W x u = W x v.
Proof.
move=> E F T f Ts lef.
have oe: order (extension_order E F) by fprops.
have oo: order (opposite_order (extension_order E F)) by fprops.
have sTe: sub T (substrate (extension_order E F)) by aw.
have sT:sub T (substrate (opposite_order (extension_order E F))) by aw; fprops.
move: lef; aw.
move=> [[fs ub] hyp] u v x uT vT xs ys.
by rewrite (extension_order_pr (ub _ uT) xs) (extension_order_pr (ub _ vT) ys).
Qed.

Lemma sup_extension_order2: forall E F T,
  sub T (set_of_sub_functions E F) ->
  (forall u v x, inc u T -> inc v T -> inc x (source u) -> inc x (source v) ->
    W x u = W x v) ->
  exists x, least_upper_bound (opposite_order (extension_order E F)) T x &
     (source x = unionf T source) &
     (range (graph x) = unionf T (fun u => (range (graph u))))&
     (graph x) = unionf T graph.
Proof.
move=> E F T Ts ag.
have fp: forall i, inc i T -> function_prop i (source i) F.
  by move=> i iT; move: (Ts _ iT); bw; rewrite / function_prop; intuition.
have ag1: forall i j, inc i T -> inc j T ->
    agrees_on (intersection2 (source i) (source j)) i j.
  move=> i j iT jT; split; first by apply intersection2sub_first.
  split; first by apply intersection2sub_second.
  move=> a; aw; move=> [ai aj]; by apply ag.
move:(extension_covering1 fp ag1)=> [g [[fg [sf tg]] [gg [rg vg]]]].
have eo: order (extension_order E F) by fprops.
have oo: order (opposite_order (extension_order E F)) by fprops.
have ssEF:set_of_sub_functions E F =
    substrate (opposite_order (extension_order E F)) by aw.
have Tso: sub T (substrate (opposite_order (extension_order E F))) by aw.
have sTe: sub T (substrate (extension_order E F)) by aw.
have xss:inc g (set_of_sub_functions E F).
  bw; ee.
  rewrite sf; move=> t; srw; move=> [y [yT ty]].
  by move: (Ts _ yT);bw; move=> [_ [yE _]]; apply yE.
exists g;ee; aw.
split.
  red;rewrite -ssEF; ee.
  move=> y yT; move:(Ts _ yT) => yss.
  rewrite extension_order_pr2; ee.
  by move => t ta; rewrite gg; union_tac.
move=> z; rewrite /upper_bound -ssEF.
rewrite extension_order_pr2; move=> [zss hyp]; ee.
move=> t; rewrite gg; srw; move=> [y [yT tgg]].
by move: (hyp _ yT); rewrite extension_order_pr2; move => [_[_]]; apply.
Qed.

Supremum and infinum of the range of a function
Definition is_sup_fun r f x := least_upper_bound r (image_of_fun f) x.
Definition is_inf_fun r f x := greatest_lower_bound r (image_of_fun f) x.

Lemma is_sup_fun_pr: forall r f x, order r -> substrate r = target f ->
  is_function f ->
  is_sup_fun r f x = (inc x (target f)
    & (forall a, inc a (source f) -> gle r (W a f) x)
    & forall z, inc z (target f) -> (forall a, inc a (source f)
      -> gle r (W a f) z) -> gle r x z).
Proof.
move=> r f x or sr ff.
have si: sub (image_of_fun f) (substrate r).
   by rewrite sr; apply sub_image_target.
rewrite /is_sup_fun; aw.
rewrite /upper_bound sr /image_of_fun.
have gf: is_graph (graph f) by fprops.
apply iff_eq.
  move=> [[xt p1] p2].
  split=>//; split.
    by move=> a afs; apply p1; aw; ex_tac; graph_tac.
  move=> z zt sh; apply p2; ee.
  by move=> y; aw; move => [u [usf JG]]; rewrite -(W_pr ff JG); apply sh.
move=> [xt [p1 p2]]; split.
  split=>//.
  by move=> y; aw; move=> [u [usf JG]]; rewrite -(W_pr ff JG); apply p1.
move=> z [ztg p3].
apply p2=>//; move=> a asf; apply p3; aw; ex_tac; graph_tac.
Qed.

Lemma is_inf_fun_pr: forall r f x, order r -> substrate r = target f ->
  is_function f ->
  is_inf_fun r f x
 = (inc x (target f)
   & (forall a, inc a (source f) -> gle r x (W a f))
   & forall z, inc z (target f) -> (forall a, inc a (source f)
     -> gle r z (W a f)) -> gle r z x).
Proof.
move=> r f x or sr ff.
have si: sub (image_of_fun f) (substrate r).
   by rewrite sr; apply sub_image_target.
rewrite /is_inf_fun; aw.
rewrite /lower_bound sr /image_of_fun.
have gf: is_graph (graph f) by fprops.
apply iff_eq.
  move=> [[xt p1] p2].
  split=>//; split.
    by move=> a afs; apply p1; aw; ex_tac; graph_tac.
  move=> z zt sh; apply p2; ee.
  by move=> y; aw; move => [u [usf JG]]; rewrite -(W_pr ff JG); apply sh.
move=> [xt [p1 p2]]; split.
  split=>//.
  by move=> y; aw; move=> [u [usf JG]]; rewrite -(W_pr ff JG); apply p1.
move=> z [ztg p3].
apply p2=>//; move=> a asf; apply p3; aw; ex_tac; graph_tac.
Qed.

Supremum and infinum of the range of a functional graph

Definition is_sup_graph r f x := least_upper_bound r (range f) x.
Definition is_inf_graph r f x := greatest_lower_bound r (range f) x.
Definition has_sup_graph r f := has_supremum r (range f).
Definition has_inf_graph r f := has_infimum r (range f).
Definition sup_graph r f:= supremum r (range f).
Definition inf_graph r f := infimum r (range f).

Lemma is_sup_graph_pr1: forall r f,
  order r -> sub (range f) (substrate r) -> has_sup_graph r f ->
  least_upper_bound r (range f) (sup_graph r f).
Proof.
rewrite /has_sup_graph/sup_graph/is_sup_graph=> r f or sr hs. fprops.
Qed.

Lemma is_inf_graph_pr1: forall r f,
  order r -> sub (range f) (substrate r) -> has_inf_graph r f ->
  greatest_lower_bound r (range f) (inf_graph r f).
Proof. rewrite /has_inf_graph/inf_graph/is_inf_graph; fprops.
Qed.

Lemma is_sup_graph_pr: forall r f x, order r -> sub (range f) (substrate r) ->
  fgraph f ->
  is_sup_graph r f x = (inc x (substrate r)
   & (forall a, inc a (domain f) -> gle r (V a f) x)
   & forall z, inc z (substrate r) -> (forall a, inc a (domain f)
     -> gle r (V a f) z) -> gle r x z).
Proof.
move=> r f x or sr fg.
rewrite /is_sup_graph; aw; rewrite /upper_bound.
have gf: is_graph f by fprops.
apply iff_eq.
   move=> [[xs p1] p2]; split=>//.
   split; first by move=> a ad; apply p1; fprops.
   move=> z zs p; apply p2.
  split=>//.
  move=> y; dw; move=> [t Jg]; move: (pr2_V fg Jg); aw; move=> ->;
  by apply p; ex_tac.
move=> [xs [p1 p2]]; split.
  split=>//; move=> y; dw; move=> [z Jg]; move: (pr2_V fg Jg); aw.
  move=> ->; apply p1; aw; ex_tac.
move=> z [zr p3]; apply p2 =>//; move=> a ad; apply p3; fprops.
Qed.

Lemma is_inf_graph_pr: forall r f x, order r -> sub (range f) (substrate r) ->
  fgraph f ->
  is_inf_graph r f x = (inc x (substrate r)
   & (forall a, inc a (domain f) -> gle r x (V a f))
   & forall z, inc z (substrate r) -> (forall a, inc a (domain f)
     -> gle r z (V a f)) -> gle r z x).
Proof.
move=> r f x or sr fg.
rewrite /is_inf_graph; aw; rewrite /lower_bound.
have gf: is_graph f by fprops.
apply iff_eq.
   move=> [[xs p1] p2]; split=>//.
   split; first by move=> a ad; apply p1; fprops.
   move=> z zs p; apply p2.
  split=>//.
  move=> y; dw; move=> [t Jg]; move: (pr2_V fg Jg); aw; move=> ->;
  by apply p; ex_tac.
move=> [xs [p1 p2]]; split.
  split=>//; move=> y; dw; move=> [z Jg]; move: (pr2_V fg Jg); aw.
  move=> ->; apply p1; aw; ex_tac.
move=> z [zr p3]; apply p2 =>//; move=> a ad; apply p3; fprops.
Qed.

Theorem compare_inf_sup1: forall r A, order r -> sub A (substrate r) ->
  has_supremum r A -> has_infimum r A ->
  A = emptyset ->
  (greatest_element r (infimum r A) & least_element r (supremum r A)).
Proof.
move=> r A or As hs hi Ae.
move: (supremum_pr1 hs)(infimum_pr1 hi).
rewrite Ae greatest_lower_bound_emptyset // least_upper_bound_emptyset //.
Qed.

Theorem compare_inf_sup2: forall r A, order r -> sub A (substrate r) ->
  has_supremum r A -> has_infimum r A ->
  nonempty A -> gle r (infimum r A) (supremum r A).
Proof.
move=> r A or As hs hi [x xA].
move: (supremum_pr1 hs)(infimum_pr1 hi).
aw; move=> [[uE p1] _ ] [[vE p2] _].
move: (p1 _ xA)(p2 _ xA)=> q1 q2.
order_tac.
Qed.

Theorem sup_increasing: forall r A B, order r -> sub A (substrate r) ->
  sub B (substrate r) -> sub A B ->
  has_supremum r A -> has_supremum r B->
  gle r (supremum r A) (supremum r B).
Proof.
move=> r A B or As Bs AB sA sB.
move: (supremum_pr1 sA) (supremum_pr1 sB); aw; move=> [uA p1][uB p2].
have aux:(upper_bound r A (supremum r B)).
red; red in uB; ee.
by apply p1.
Qed.

Theorem inf_decreasing: forall r A B, order r -> sub A (substrate r) ->
  sub B (substrate r) -> sub A B ->
  has_infimum r A -> has_infimum r B ->
  gge r (infimum r A) (infimum r B).
Proof.
move=> r A B or As Bs AB sA sB.
move: (infimum_pr1 sA) (infimum_pr1 sB); aw; move=> [uA p1][uB p2].
have aux:(lower_bound r A (infimum r B)).
red; red in uB; ee.
by apply p1.
Qed.

Lemma sup_increasing1: forall r f j,
  order r -> fgraph f -> sub (range f) (substrate r) -> sub j (domain f) ->
  has_sup_graph r f ->
  has_sup_graph r (restr f j) ->
  gle r (sup_graph r (restr f j)) (sup_graph r f).
Proof.
rewrite /has_sup_graph/is_sup_graph/sup_graph => r f j or fg sr sj hs1 hs2.
have gr: is_graph (restr f j) by move:(restr_fgraph j fg)=>[h _].
have srr: sub (range (restr f j)) (range f).
  move=> t; dw; fprops; move=> [x Jg]; move:(restr_sub Jg)=> h; ex_tac.
apply sup_increasing=>//.
by apply sub_trans with (range f).
Qed.

Lemma inf_decreasing1: forall r f j,
  order r -> fgraph f -> sub (range f) (substrate r) -> sub j (domain f) ->
  has_inf_graph r f -> has_inf_graph r (restr f j) ->
  gge r (inf_graph r (restr f j)) (inf_graph r f).
Proof.
rewrite /has_inf_graph/is_inf_graph/inf_graph => r f j or fg sr sj hs1 hs2.
have gr: is_graph (restr f j) by move:(restr_fgraph j fg)=>[h _].
have srr: sub (range (restr f j)) (range f).
  move=> t; dw; fprops; move=> [x Jg]; move:(restr_sub Jg)=> h; ex_tac.
apply inf_decreasing=>//.
by apply sub_trans with (range f).
Qed.

Lemma sup_increasing2: forall r f f',
  order r -> fgraph f -> fgraph f' -> domain f = domain f' ->
  sub (range f) (substrate r) -> sub (range f') (substrate r) ->
  has_sup_graph r f -> has_sup_graph r f'->
  (forall x , inc x (domain f) -> gle r (V x f) (V x f')) ->
  gle r (sup_graph r f) (sup_graph r f').
Proof.
move=> r f f' or gf gf' df sr sr' hs hs' ale.
move: (is_sup_graph_pr1 or sr hs)(is_sup_graph_pr1 or sr' hs').
move: (is_sup_graph_pr (sup_graph r f) or sr gf).
move:(is_sup_graph_pr (sup_graph r f') or sr' gf').
rewrite /is_sup_graph. move=> -> ->.
move=> [sgr [p1 p2]][sgr' [p1' p2']].
apply p2=>//.
move=> a ad.
apply order_transitivity with (V a f')=>//; [apply ale=>//|apply p1']; ue.
Qed.

Lemma inf_increasing2: forall r f f',
  order r -> fgraph f -> fgraph f' -> domain f = domain f' ->
  sub (range f) (substrate r) -> sub (range f') (substrate r) ->
  has_inf_graph r f ->
  has_inf_graph r f'->
  (forall x , inc x (domain f) -> gle r (V x f) (V x f')) ->
  gle r (inf_graph r f) (inf_graph r f').
Proof.
move=> r f f' or gf gf' df sr sr' hs hs' ale.
move: (is_inf_graph_pr1 or sr hs)(is_inf_graph_pr1 or sr' hs').
move: (is_inf_graph_pr (inf_graph r f) or sr gf).
move:(is_inf_graph_pr (inf_graph r f') or sr' gf').
rewrite /is_inf_graph. move=> -> ->.
move=> [sgr [p1 p2]][sgr' [p1' p2']].
apply p2'=>//.
move=> a ad.
apply order_transitivity with (V a f)=>//;[apply p1 | apply ale=>//]; ue.
Qed.

Associativity of sup and inf

Lemma sup_distributive: forall r f c x,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  covering c (domain f) ->
  (forall l, inc l (domain c) -> has_sup_graph r (restr f (V l c))) ->
  is_sup_graph r f x =
  is_sup_graph r (L (domain c) (fun l => sup_graph r (restr f (V l c)))) x.
Proof.
move=> r f c x or gf sr [fcc sdu] alsup.
have grf: is_graph f by fprops.
have fgr: forall a, fgraph (restr f (V a c)) by move=> a; fprops.
have grr: forall a, is_graph (restr f (V a c)).
  by move=> a; move: (fgr a); fprops.
set g:= L _ _.
have fgg:fgraph g by rewrite /g; gprops.
have ranr: forall a, (sub (range (restr f (V a c))) (substrate r)).
  move=> a t; dw;move=> [z zr]; apply sr; move:(restr_sub zr)=> h; ex_tac.
have srh: (sub (range g) (substrate r)).
  move=> t. rewrite /g; bw. move=> [b [bd st]].
  move: (is_sup_graph_pr1 or (ranr b) (alsup _ bd)).
  by rewrite st; aw; move=> [[? _] _].
rewrite is_sup_graph_pr //is_sup_graph_pr // /g; bw.
apply iff_eq; move=> [xsr [p1 p2]]; split=>//.
  split.
    move=> a adc; bw.
    move: (is_sup_graph_pr1 or (ranr a) (alsup _ adc)).
    rewrite least_upper_bound_pr //; move=> [_ ]; apply.
    split=>//; move=>y; dw; move=> [z];rewrite restr_inc_rw.
    aw; move=> [Jf zV]; move: (in_graph_V gf Jf); aw=> aux.
    by rewrite (pr2_def aux); apply p1; ex_tac.
  move=> z zr hyp.
  apply p2 =>//; move=> a ad.
  move: (sdu _ ad); srw; move=> [y [yd aV]].
  apply order_transitivity with (sup_graph r (restr f (V y c)))=>//.
    move: (is_sup_graph_pr1 or (ranr y) (alsup _ yd)).
    rewrite least_upper_bound_pr //; move=> [[ _ p] _]; apply p.
    srw; rewrite restr_domain //; exists a.
   split; [ apply intersection2_inc=>// | rewrite restr_ev1 //].
  by move: (hyp _ yd); bw.
split.
  move=> a ad; move: (sdu _ ad); srw; move=> [y [yd aV]].
  move: (p1 _ yd); bw=> aux.
  apply order_transitivity with (sup_graph r (restr f (V y c)))=> //.
  move: (is_sup_graph_pr1 or (ranr y) (alsup _ yd)).
  rewrite least_upper_bound_pr //.
  move=> [[_ p] _]; apply p; srw; exists a.
  by rewrite restr_domain //;split;[ apply intersection2_inc| rewrite restr_ev1].
move=> z zr h; apply p2 =>//.
move=> a ad; bw.
move: (is_sup_graph_pr1 or (ranr a) (alsup _ ad)).
rewrite least_upper_bound_pr //; move=> [_ p]; apply p.
split=>// y; dw; move=> [t]; rewrite restr_inc_rw //.
aw;move=> [Jf tV];move: (in_graph_V gf Jf); aw.
move=> h1; rewrite (pr2_def h1).
apply h; ex_tac.
Qed.

Lemma inf_distributive: forall r f c x,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  covering c (domain f) ->
  (forall l, inc l (domain c) -> has_inf_graph r (restr f (V l c))) ->
  is_inf_graph r f x =
  is_inf_graph r (L (domain c) (fun l => inf_graph r (restr f (V l c)))) x.
Proof.
move=> r f c x or gf sr [fcc sdu] alinf.
have grf: is_graph f by fprops.
have fgr: forall a, fgraph (restr f (V a c)) by move=> a; fprops.
have grr: forall a, is_graph (restr f (V a c)).
  by move=> a; move: (fgr a); fprops.
set g:= L _ _.
have fgg:fgraph g by rewrite /g; gprops.
have ranr: forall a, (sub (range (restr f (V a c))) (substrate r)).
  move=> a t; dw;move=> [z zr]; apply sr; move:(restr_sub zr)=> h; ex_tac.
have srh: (sub (range g) (substrate r)).
  move=> t. rewrite /g; bw. move=> [b [bd st]].
  move: (is_inf_graph_pr1 or (ranr b) (alinf _ bd)).
  by rewrite st; aw; move=> [[? _] _].
rewrite is_inf_graph_pr //is_inf_graph_pr // /g; bw.
apply iff_eq; move=> [xsr [p1 p2]]; split=>//.
  split.
    move=> a adc; bw.
    move: (is_inf_graph_pr1 or (ranr a) (alinf _ adc)).
    rewrite greatest_lower_bound_pr //; move=> [_ ]; apply.
    split=>//; move=>y; dw; move=> [z];rewrite restr_inc_rw.
    aw; move=> [Jf zV]; move: (in_graph_V gf Jf); aw=> aux.
    by rewrite (pr2_def aux); apply p1; ex_tac.
  move=> z zr hyp.
  apply p2 =>//; move=> a ad.
  move: (sdu _ ad); srw; move=> [y [yd aV]].
  apply order_transitivity with (inf_graph r (restr f (V y c)))=>//.
    by move: (hyp _ yd); bw.
  move: (is_inf_graph_pr1 or (ranr y) (alinf _ yd)).
  rewrite greatest_lower_bound_pr //; move=> [[ _ p] _]; apply p.
  srw; rewrite restr_domain //; exists a.
  split; [ apply intersection2_inc=>// | rewrite restr_ev1 //].
split.
  move=> a ad; move: (sdu _ ad); srw; move=> [y [yd aV]].
  move: (p1 _ yd); bw=> aux.
  apply order_transitivity with (inf_graph r (restr f (V y c)))=> //.
  move: (is_inf_graph_pr1 or (ranr y) (alinf _ yd)).
  rewrite greatest_lower_bound_pr //.
  move=> [[_ p] _]; apply p; srw; exists a.
  by rewrite restr_domain //;split;[ apply intersection2_inc| rewrite restr_ev1].
move=> z zr h; apply p2 =>//.
move=> a ad; bw.
move: (is_inf_graph_pr1 or (ranr a) (alinf _ ad)).
rewrite greatest_lower_bound_pr //; move=> [_ p]; apply p.
split=>// y; dw; move=> [t]; rewrite restr_inc_rw //.
aw;move=> [Jf tV];move: (in_graph_V gf Jf); aw.
move=> h1; rewrite (pr2_def h1).
apply h; ex_tac.
Qed.

Lemma sup_distributive1: forall r f c,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  covering c (domain f) ->
  (forall l, inc l (domain c) -> has_sup_graph r (restr f (V l c))) ->
  has_sup_graph r f =
  has_sup_graph r (L (domain c) (fun l => sup_graph r (restr f (V l c)))) .
Proof.
move=> r f c or ff sr co alsup.
apply iff_eq; move=> [x xs];
move: (sup_distributive x or ff sr co alsup);
rewrite /is_sup_graph; exists x; ue.
Qed.

Lemma inf_distributive1: forall r f c,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  covering c (domain f) ->
  (forall l, inc l (domain c) -> has_inf_graph r (restr f (V l c))) ->
  has_inf_graph r f =
  has_inf_graph r (L (domain c) (fun l => inf_graph r (restr f (V l c)))) .
Proof.
move=> r f c or ff sr co alsup.
apply iff_eq; move=> [x xs];
move: (inf_distributive x or ff sr co alsup);
rewrite /is_inf_graph; exists x; ue.
Qed.

Theorem sup_distributive2: forall r f c,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  covering c (domain f) ->
  (forall l, inc l (domain c) -> has_sup_graph r (restr f (V l c))) ->
  ((has_sup_graph r f =
    has_sup_graph r (L (domain c) (fun l => sup_graph r (restr f (V l c))))) &
  (has_sup_graph r f -> sup_graph r f =
    sup_graph r (L (domain c) (fun l => sup_graph r (restr f (V l c)))))).
Proof.
move=> r f c or fgf sr co alsup.
split; first by apply sup_distributive1=>//.
move=> hyp; rewrite /sup_graph.
set (g:= L (domain c) (fun l => sup_graph r (restr f (V l c)))).
move: (is_sup_graph_pr1 or sr hyp)=> lu.
have lu': (least_upper_bound r (range f) (supremum r (range f))).
  by apply supremum_pr1.
move: (sup_distributive (sup_graph r f) or fgf sr co alsup).
rewrite /is_sup_graph -/g => aux.
have lug: least_upper_bound r (range g) (sup_graph r f).
   by rewrite -aux.
clear aux.
set (x:= sup_graph r f) in *.
rewrite -(supremum_unique or lu lu').
have lug': least_upper_bound r (range g) (supremum r (range g)).
  by apply supremum_pr1; exists x.
by rewrite -(supremum_unique or lug lug').
Qed.

Theorem inf_distributive2: forall r f c,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  covering c (domain f) ->
  (forall l, inc l (domain c) -> has_inf_graph r (restr f (V l c))) ->
  ((has_inf_graph r f =
    has_inf_graph r (L (domain c) (fun l => inf_graph r (restr f (V l c))))) &
  (has_inf_graph r f -> inf_graph r f =
    inf_graph r (L (domain c) (fun l => inf_graph r (restr f (V l c)))))).
Proof.
move=> r f c or fgf sr co alsup.
split; first by apply inf_distributive1=>//.
move=> hyp; rewrite /inf_graph.
set (g:= L (domain c) (fun l => inf_graph r (restr f (V l c)))).
move: (is_inf_graph_pr1 or sr hyp)=> lu.
have lu': (greatest_lower_bound r (range f) (infimum r (range f))).
  by apply infimum_pr1.
move: (inf_distributive (inf_graph r f) or fgf sr co alsup).
rewrite /is_inf_graph -/g => aux.
have lug: greatest_lower_bound r (range g) (inf_graph r f).
   by rewrite -aux.
clear aux.
set (x:= inf_graph r f) in *.
rewrite -(infimum_unique or lu lu').
have lug': greatest_lower_bound r (range g) (infimum r (range g)).
  by apply infimum_pr1; exists x.
by rewrite -(infimum_unique or lug lug').
Qed.

Definition partial_fun f x m := restr f (product x (singleton m)).

Lemma sup_distributive3: forall r f x y,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  domain f = product x y ->
  (forall m, inc m y -> has_sup_graph r (partial_fun f x m)) ->
  ((has_sup_graph r f =
    has_sup_graph r (L y (fun m => sup_graph r (partial_fun f x m)))) &
  (has_sup_graph r f -> sup_graph r f =
    sup_graph r (L y (fun m => sup_graph r (partial_fun f x m))))).
Proof.
move=> r f x y or fgf sr df alsup.
set (c:= L y (fun m => product x (singleton m))).
have co: covering c (domain f).
  split; first by rewrite /c; gprops.
  rewrite /c df=> t; aw; move=> [pt [Px Qy]].
  by apply unionb_inc with (Q t); bw; apply product_inc; fprops.
have dc: y = domain c by rewrite /c; bw.
have pfx: forall m, inc m y -> partial_fun f x m = restr f (V m c).
  move=> m my; rewrite /partial_fun /c; bw.
have hsg: forall l, inc l (domain c) -> has_sup_graph r (restr f (V l c)).
  by move=> l; rewrite -dc=> ld; rewrite - pfx //;apply alsup.
have aux: ( L (domain c) (fun l => sup_graph r (restr f (V l c))) =
    L y (fun m : Set => sup_graph r (partial_fun f x m))).
  by rewrite -dc; apply L_exten1=>//; move=> t ty; rewrite pfx.
by rewrite - aux; apply sup_distributive2.
Qed.

Lemma inf_distributive3: forall r f x y,
  order r -> fgraph f -> sub (range f) (substrate r) ->
  domain f = product x y ->
  (forall m, inc m y -> has_inf_graph r (partial_fun f x m)) ->
  ((has_inf_graph r f =
    has_inf_graph r (L y (fun m => inf_graph r (partial_fun f x m)))) &
  (has_inf_graph r f -> inf_graph r f =
    inf_graph r (L y (fun m => inf_graph r (partial_fun f x m))))).
Proof.
move=> r f x y or fgf sr df alinf.
set (c:= L y (fun m => product x (singleton m))).
have co: covering c (domain f).
  split; first by rewrite /c; gprops.
  rewrite /c df=> t; aw; move=> [pt [Px Qy]].
  by apply unionb_inc with (Q t); bw; apply product_inc; fprops.
have dc: y = domain c by rewrite /c; bw.
have pfx: forall m, inc m y -> partial_fun f x m = restr f (V m c).
  move=> m my; rewrite /partial_fun /c; bw.
have hsg: forall l, inc l (domain c) -> has_inf_graph r (restr f (V l c)).
  by move=> l; rewrite -dc=> ld; rewrite - pfx //;apply alinf.
have aux: ( L (domain c) (fun l => inf_graph r (restr f (V l c))) =
    L y (fun m : Set => inf_graph r (partial_fun f x m))).
  by rewrite -dc; apply L_exten1=>//; move=> t ty; rewrite pfx.
by rewrite - aux; apply inf_distributive2.
Qed.

Theorem sup_in_product: forall g A,
  let f := fam_of_substrates g in
  let Ai:= fun i => (image_by_fun (pr_i f i) A) in
    let has_sup :=
      forall i, inc i (domain g) -> has_supremum (V i g) (Ai i) in
    product_order_axioms g -> sub A (productb f) ->
    ((has_sup = has_supremum (product_order g) A) &
      (has_sup -> supremum (product_order g) A =
        L (domain g) (fun i => supremum (V i g) (Ai i)))).
Proof.
move=> g A f Ai hs po Ab.
move: (po)=> [fg allo].
have fgf: fgraph f by rewrite /f/fam_of_substrates; gprops.
have df: domain f = domain g by rewrite /f/fam_of_substrates; bw.
have als: forall i, inc i (domain g) -> substrate (V i g) = V i f.
  by move=> i id; rewrite /f/fam_of_substrates; bw.
have Asi: forall i, inc i (domain g) -> sub A (source (pr_i f i)).
   by move=> i idg; rewrite /pr_i; aw.
have Ais: forall i, inc i (domain g) -> sub (Ai i) (substrate (V i g)).
  move=> i id; rewrite als /Ai //.
  have fp: is_function (pr_i f i) by apply pri_function =>//; ue.
  move: (Asi _ id) =>As.
  move=> t; aw; move=> [u [uA]] <-.
  have us:inc u (source (pr_i f i)) by apply As.
  have ->: (V i f = target (pr_i f i)) by rewrite /pr_i; aw.
  apply inc_W_target=>//.
have p1: (hs -> (forall i, inc i (domain g) ->
    least_upper_bound (V i g) (Ai i) (supremum (V i g) (Ai i)))).
  move => hts i idg; apply supremum_pr1.
  apply hts =>//.
set (mysup:=L (domain g) (fun i : Set => supremum (V i g) (Ai i))).
have opg: order (product_order g) by apply product_order_order.
have hlu: hs -> least_upper_bound (product_order g) A mysup.
  move=> hst.
  have mp: inc mysup (productb f).
    rewrite /mysup;aw; split; first by gprops.
    bw; split; first by ue.
    move=> i idg; bw.
    rewrite -als //; apply inc_supremum_substrate.
        by apply allo.
      by apply Ais.
    by apply hst.
  aw;split.
    split; first by rewrite product_order_substrate.
    move=> y yA; rewrite product_order_related //.
    split; first by apply Ab.
    split=>//; move=> i idg; rewrite /mysup; bw.
    move: (supremum_pr (allo _ idg) (Ais _ idg) (hst _ idg)).
    move=> [[_ h]_]; apply h.
    rewrite /Ai; aw;try (apply (Asi _ idg)); try (apply pri_function=>//; ue).
    by ex_tac; rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ yA)).
  move=> z [zs zp]; rewrite product_order_related //.
  split=>//.
  split; first by rewrite product_order_substrate in zs.
  move=> i idg.
  have fi: is_function (pr_i f i) by apply pri_function=>//; ue.
  move: (supremum_pr (allo _ idg) (Ais _ idg) (hst _ idg)).
  rewrite /mysup; bw; move=> [_ h]; apply h.
  split; first by move: zs; aw; move=> [_ [_]]; apply.
  move: (Asi _ idg) => As.
  move=> y; rewrite /Ai; aw; move=> [u [uA]] <-.
  rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ uA)).
  by move: (zp _ uA); bw; move=> [_[_]]; apply; ue.
have hsu: hs -> has_supremum (product_order g) A.
  by move=> hst; exists mysup; apply hlu.
have g2: hs-> supremum (product_order g) A = mysup.
  move => hst; move:(hlu hst) => s1; move:( supremum_pr1 (hsu hst)) => s2.
  by apply (supremum_unique opg s2 s1).
split=>//.
apply iff_eq =>//.
have sAs: sub A (substrate (product_order g)) by aw.
move=> [x]; rewrite least_upper_bound_pr //; move => [[xs ub] lub].
move=> i idg.
move: (allo _ idg) => p2; move: (Ais _ idg)=> p3; move: (Asi _ idg)=> p4.
have fi: is_function (pr_i f i) by apply pri_function=>//; ue.
exists (V i x); rewrite least_upper_bound_pr//.
move: xs; aw;move=> [fgx [dx iVs]].
split.
  split; first by apply iVs.
  rewrite /Ai; move=> y; aw ; move=> [u [uA]] <-.
  rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ uA)).
  by move: (ub _ uA); bw; move=> [_[_]]; apply; ue.
move=> z [zs h4].
set (y:= L (domain g) (fun j=> Yo(j = i) z (V j x))).
have ->: (z = V i y) by rewrite /y; bw; Ytac0.
have yp: inc y (productb f).
  aw; rewrite /y; ee; bw; try ue.
  move=> k kd; bw; rewrite -als//; Ytac ki; first by rewrite ki; apply zs.
  by apply iVs.
have aux: (upper_bound (product_order g) A y).
  split; first by rewrite product_order_substrate.
  move=> t tA; bw; ee.
  move => k kd; rewrite /y; bw.
  move: (ub _ tA); bw; move=> [_[_ h5]]; Ytac ki; last by apply h5.
  rewrite ki; apply h4.
  rewrite / Ai; aw; ex_tac.
  by rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ tA)).
by move: (lub _ aux); bw; move=> [_[_]]; apply.
Qed.

Theorem inf_in_product: forall g A,
  let f := fam_of_substrates g in
  let Ai:= fun i => (image_by_fun (pr_i f i) A) in
    let has_inf :=
      forall i, inc i (domain g) -> has_infimum (V i g) (Ai i) in
    product_order_axioms g -> sub A (productb f) ->
    ((has_inf = has_infimum (product_order g) A) &
      (has_inf -> infimum (product_order g) A =
        L (domain g) (fun i => infimum (V i g) (Ai i)))).
Proof.
move=> g A f Ai hs po Ab.
move: (po)=> [fg allo].
have fgf: fgraph f by rewrite /f/fam_of_substrates; gprops.
have df: domain f = domain g by rewrite /f/fam_of_substrates; bw.
have als: forall i, inc i (domain g) -> substrate (V i g) = V i f.
  by move=> i id; rewrite /f/fam_of_substrates; bw.
have Asi: forall i, inc i (domain g) -> sub A (source (pr_i f i)).
   by move=> i idg; rewrite /pr_i; aw.
have Ais: forall i, inc i (domain g) -> sub (Ai i) (substrate (V i g)).
  move=> i id; rewrite als /Ai //.
  have fp: is_function (pr_i f i) by apply pri_function =>//; ue.
  move: (Asi _ id) =>As.
  move=> t; aw; move=> [u [uA]] <-.
  have us:inc u (source (pr_i f i)) by apply As.
  have ->: (V i f = target (pr_i f i)) by rewrite /pr_i; aw.
  apply inc_W_target=>//.
have p1: (hs -> (forall i, inc i (domain g) ->
    greatest_lower_bound (V i g) (Ai i) (infimum (V i g) (Ai i)))).
  move => hts i idg; apply infimum_pr1.
  apply hts =>//.
set (mysup:=L (domain g) (fun i : Set => infimum (V i g) (Ai i))).
have opg: order (product_order g) by apply product_order_order.
have hlu: hs -> greatest_lower_bound (product_order g) A mysup.
  move=> hst.
  have mp: inc mysup (productb f).
    rewrite /mysup;aw; split; first by gprops.
    bw; split; first by ue.
    move=> i idg; bw.
    rewrite -als //; apply inc_infimum_substrate.
        by apply allo.
      by apply Ais.
    by apply hst.
  aw;split.
    split; first by rewrite product_order_substrate.
    move=> y yA; rewrite product_order_related //.
    split=>//;split; first by apply Ab.
    move=> i idg; rewrite /mysup; bw.
    move: (infimum_pr (allo _ idg) (Ais _ idg) (hst _ idg)).
    move=> [[_ h]_]; apply h.
    rewrite /Ai; aw;try (apply (Asi _ idg)); try (apply pri_function=>//; ue).
    by ex_tac; rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ yA)).
  move=> z [zs zp]; rewrite product_order_related //.
  split; first by rewrite product_order_substrate in zs.
  split=>//.
  move=> i idg.
  have fi: is_function (pr_i f i) by apply pri_function=>//; ue.
  move: (infimum_pr (allo _ idg) (Ais _ idg) (hst _ idg)).
  rewrite /mysup; bw; move=> [_ h]; apply h.
  split; first by move: zs; aw; move=> [_ [_]]; apply.
  move: (Asi _ idg) => As.
  move=> y; rewrite /Ai; aw; move=> [u [uA]] <-.
  rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ uA)).
  by move: (zp _ uA); bw; move=> [_[_]]; apply; ue.
have hsu: hs -> has_infimum (product_order g) A.
  by move=> hst; exists mysup; apply hlu.
have g2: hs-> infimum (product_order g) A = mysup.
  move => hst; move:(hlu hst) => s1; move:(infimum_pr1 (hsu hst)) => s2.
  by apply (infimum_unique opg s2 s1).
split=>//.
apply iff_eq =>//.
have sAs: sub A (substrate (product_order g)) by aw.
move=> [x]; rewrite greatest_lower_bound_pr //; move => [[xs ub] lub].
move=> i idg.
move: (allo _ idg) => p2; move: (Ais _ idg)=> p3; move: (Asi _ idg)=> p4.
have fi: is_function (pr_i f i) by apply pri_function=>//; ue.
exists (V i x); rewrite greatest_lower_bound_pr//.
move: xs; aw;move=> [fgx [dx iVs]].
split.
  split; first by apply iVs.
  rewrite /Ai; move=> y; aw ; move=> [u [uA]] <-.
  rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ uA)).
  by move: (ub _ uA); bw; move=> [_[_]]; apply; ue.
move=> z [zs h4].
set (y:= L (domain g) (fun j=> Yo(j = i) z (V j x))).
have ->: (z = V i y) by rewrite /y; bw; Ytac0.
have yp: inc y (productb f).
  aw; rewrite /y; ee; bw; try ue.
  move=> k kd; bw; rewrite -als//; Ytac ki; last by apply iVs.
  by rewrite ki;apply zs.
have aux: (lower_bound (product_order g) A y).
  split; first by rewrite product_order_substrate.
  move=> t tA; bw; ee.
  move => k kd; rewrite /y; bw.
  move: (ub _ tA); bw; move=> [_[_ h5]]; Ytac ki; last by apply h5.
  rewrite ki; apply h4; rewrite / Ai; aw; ex_tac.
  by rewrite -df in idg; rewrite (pri_W fgf idg (Ab _ tA)).
by move: (lub _ aux); bw; move=> [_[_]]; apply.
Qed.

Theorem sup_induced1: forall r A F, order r -> sub F (substrate r) -> sub A F ->
  has_supremum r A -> has_supremum (induced_order r F) A ->
  gle r (supremum r A) (supremum (induced_order r F) A).
Proof.
move=> r A F or Fs AF sA siA.
have oi: order (induced_order r F) by fprops.
have As:sub A (substrate r) by apply sub_trans with F.
have Asi: sub A (substrate (induced_order r F)) by aw.
move: (supremum_pr1 sA) (supremum_pr1 siA).
set (u:= supremum r A).
set (v:=supremum (induced_order r F) A).
aw; move=> [ub lub][[us ubi] lubi]; apply lub.
move: us; aw => vF.
split; first by apply Fs.
by move=> y yA; move: (ubi _ yA); aw; apply AF.
Qed.

Theorem inf_induced1: forall r A F, order r -> sub F (substrate r) -> sub A F ->
  has_infimum r A -> has_infimum (induced_order r F) A ->
  gge r (infimum r A) (infimum (induced_order r F) A).
Proof.
move=> r A F or Fs AF sA siA.
have oi: order (induced_order r F) by fprops.
have As:sub A (substrate r) by apply sub_trans with F.
have Asi: sub A (substrate (induced_order r F)) by aw.
move: (infimum_pr1 sA) (infimum_pr1 siA).
set (u:= infimum r A).
set (v:=infimum (induced_order r F) A).
aw; move=> [ub lub][[us ubi] lubi]; apply lub.
move: us; aw => vF.
split; first by apply Fs.
by move=> y yA; move: (ubi _ yA); aw; apply AF.
Qed.

Theorem sup_induced2: forall r A F, order r -> sub F (substrate r) -> sub A F ->
  has_supremum r A -> inc (supremum r A) F ->
  (has_supremum (induced_order r F) A &
    supremum r A = supremum (induced_order r F) A).
Proof.
move=> r A F or Fs AF hsA sAF.
have oi: order (induced_order r F) by fprops.
have As: sub A (substrate r) by apply sub_trans with F.
have Asi:sub A (substrate (induced_order r F)) by aw.
move:(supremum_pr1 hsA).
set (u:= supremum r A).
move=> lub.
have lubi: least_upper_bound (induced_order r F) A u.
  move: lub; aw; move=> [[us ub] lub].
  split.
    split; aw; move=> y yA; aw; [ by apply ub |by apply AF].
  move=> z [zF];awi zF=>// zub.
  aw;apply lub; split; first by apply Fs.
  by move=> y yA; move: (zub _ yA); aw; apply AF.
have aux: has_supremum (induced_order r F) A by exists u.
move: (supremum_pr1 aux) =>lubi'.
split=>//;apply (supremum_unique oi lubi lubi').
Qed.

Theorem inf_induced2: forall r A F, order r -> sub F (substrate r) -> sub A F ->
  has_infimum r A -> inc (infimum r A) F ->
  (has_infimum (induced_order r F) A &
    infimum r A = infimum (induced_order r F) A).
Proof.

move=> r A F or Fs AF hsA sAF.
have oi: order (induced_order r F) by fprops.
have As: sub A (substrate r) by apply sub_trans with F.
have Asi:sub A (substrate (induced_order r F)) by aw.
move:(infimum_pr1 hsA).
set (u:= infimum r A).
move=> lub.
have lubi: greatest_lower_bound (induced_order r F) A u.
  move: lub; aw; move=> [[us ub] lub].
  split.
    split; aw; move=> y yA; aw; [ by apply ub |by apply AF].
  move=> z [zF];awi zF=>// zub.
  aw;apply lub; split; first by apply Fs.
  by move=> y yA; move: (zub _ yA); aw; apply AF.
have aux: has_infimum (induced_order r F) A by exists u.
move: (infimum_pr1 aux) =>lubi'.
split=>//;apply (infimum_unique oi lubi lubi').
Qed.

Directed sets


Definition right_directed r :=
  order r & forall x, forall y, inc x (substrate r) -> inc y (substrate r) ->
    bounded_above r (doubleton x y).
Definition left_directed r :=
  order r & forall x, forall y, inc x (substrate r) -> inc y (substrate r) ->
    bounded_below r (doubleton x y).

Lemma right_directed_pr: forall r,
  right_directed r = (order r &
    forall x, forall y, inc x (substrate r) -> inc y (substrate r) -> exists z,
      inc z (substrate r) & gle r x z & gle r y z).
Proof.
rewrite /right_directed=>r; apply iff_eq; move=> [or h]; split=>//.
  move=> x y xs ys; move: (h _ _ xs ys).
  move=> [t [ts tb]]; ex_tac.
move=> x y xs ys; move: (h _ _ xs ys).
move=> [z [zs [xz yz]]].
exists z; split =>//; move=> t td.
by case (doubleton_or td)=>->.
Qed.

Lemma left_directed_pr: forall r,
  left_directed r = (order r &
    forall x, forall y, inc x (substrate r) -> inc y (substrate r) -> exists z,
      inc z (substrate r) & gle r z x & gle r z y).
Proof.
rewrite /left_directed=>r; apply iff_eq; move=> [or h]; split=>//.
  move=> x y xs ys; move: (h _ _ xs ys).
  move=> [t [ts tb]]; ex_tac.
move=> x y xs ys; move: (h _ _ xs ys).
move=> [z [zs [xz yz]]].
exists z; split =>//; move=> t td.
by case (doubleton_or td)=>->.
Qed.

Lemma greatest_right_directed : forall r, order r ->
  (exists a, greatest_element r a ) -> right_directed r.
Proof.
move=> r or [a [asr ga]]; rewrite right_directed_pr.
split =>//; move=> x y xr yr; exists a; ee.
Qed.

Lemma least_left_directed : forall r, order r ->
  (exists a, least_element r a ) -> left_directed r.
Proof.
move=> r or [a [asr ga]]; rewrite left_directed_pr.
split =>//; move=> x y xr yr; exists a; ee.
Qed.

Lemma opposite_right_directed : forall r, is_graph r ->
  right_directed r = left_directed(opposite_order r).
Proof.
move=> r gr.
rewrite right_directed_pr left_directed_pr.
apply iff_eq; move=> [or hyp].
  have ooi: order (opposite_order r) by fprops.
  split=>//.
  aw; move => x y xs ys.
  by move: (hyp _ _ xs ys)=> [z [zs [xz yz]]]; ex_tac; aw; split.
have: order (opposite_order (opposite_order r)) by fprops.
rewrite /opposite_order inverse_graph_involutive// => odr.
split=>//; move => x y xs ys.
awi hyp =>//.
by move: (hyp _ _ xs ys) => [z]; aw; move=>[zs [xz yz]]; ex_tac; split.
Qed.

Lemma opposite_left_directed : forall r, is_graph r ->
  left_directed r = right_directed(opposite_order r).
Proof.
move=> r gr; symmetry.
have go: is_graph (opposite_order r) by fprops.
rewrite opposite_right_directed //.
rewrite /opposite_order; rewrite inverse_graph_involutive //.
Qed.

Lemma product_right_directed: forall g,
  product_order_axioms g ->
  (forall i, inc i (domain g) -> right_directed (V i g))
  -> right_directed (product_order g).
Proof.
move=> g pa; rewrite right_directed_pr => alri.
split; first by fprops.
move=> x y xs ys.
set (z:= L (domain g)(fun i => choose (fun w =>
  upper_bound (V i g) (doubleton (V i x) (V i y)) w))).
have alu: (forall i, inc i (domain g) ->
    upper_bound (V i g) (doubleton (V i x) (V i y)) (V i z)).
  move=> i idg; rewrite /z; bw.
  move: xs ys;aw; move=>[_[_ p1]] [_[_ p2]]; move:(p1 _ idg)(p2 _ idg)=> p3 p4.
  move: (alri _ idg)=> [t h]; move: (h _ _ p3 p4)=> [b bii].
  by apply choose_pr; exists b.
have zs:inc z (substrate (product_order g)).
   aw; split; first by rewrite/z; gprops.
  split; first by rewrite/z; bw.
  by move => i idg; move: (alu _ idg); move=> [us ub]; apply us.
ex_tac.
move: xs ys zs; rewrite product_order_substrate // => xs ys zs.
bw;ee.
  move=> i idg; move: (alu _ idg); move=> [_ h]; apply h; fprops.
move=> i idg; move: (alu _ idg); move=> [_ h]; apply h; fprops.
Qed.

Lemma product_left_directed: forall g,
  product_order_axioms g ->
  (forall i, inc i (domain g) -> left_directed (V i g))
  -> left_directed (product_order g).
Proof.
move=> g pa; rewrite left_directed_pr => alri.
split; first by fprops.
move=> x y xs ys.
set (z:= L (domain g)(fun i => choose (fun w =>
  lower_bound (V i g) (doubleton (V i x) (V i y)) w))).
have alu: (forall i, inc i (domain g) ->
    lower_bound (V i g) (doubleton (V i x) (V i y)) (V i z)).
  move=> i idg; rewrite /z; bw.
  move: xs ys;aw; move=>[_[_ p1]] [_[_ p2]]; move:(p1 _ idg)(p2 _ idg)=> p3 p4.
  move: (alri _ idg)=> [t h]; move: (h _ _ p3 p4)=> [b bii].
  by apply choose_pr; exists b.
have zs:inc z (substrate (product_order g)).
   aw; split; first by rewrite/z; gprops.
  split; first by rewrite/z; bw.
  by move => i idg; move: (alu _ idg); move=> [us ub]; apply us.
ex_tac.
move: xs ys zs; rewrite product_order_substrate // => xs ys zs.
bw;ee.
  move=> i idg; move: (alu _ idg); move=> [_ h]; apply h; fprops.
move=> i idg; move: (alu _ idg); move=> [_ h]; apply h; fprops.
Qed.

Lemma cofinal_right_directed: forall r A,
  right_directed r -> cofinal_set r A -> right_directed (induced_order r A).
Proof.
move=> r A; rewrite 2! right_directed_pr; move=> [or rr] [As co].
split; first by fprops.
aw; move=> x y xA yA; move: (rr _ _ (As _ xA)(As _ yA))=> [z [zr [xz yz]]].
move:(co _ zr)=> [t [tA tz]].
by ex_tac; split; aw; apply order_transitivity with z =>//.
Qed.

Lemma coinitial_left_directed: forall r A,
  left_directed r -> coinitial_set r A -> left_directed (induced_order r A).
Proof.
move=> r A; rewrite 2! left_directed_pr; move=> [or rr] [As co].
split; first by fprops.
aw; move=> x y xA yA; move: (rr _ _ (As _ xA)(As _ yA))=> [z [zr [xz yz]]].
move:(co _ zr)=> [t [tA tz]].
by ex_tac; split; aw; apply order_transitivity with z =>//.
Qed.

Theorem right_directed_maximal: forall r x,
  right_directed r -> maximal_element r x -> greatest_element r x.
Proof.
move=> r x rr [xsr ma]; split=>//.
move=> t ts.
move: rr;rewrite right_directed_pr; move => [or rr].
move:(rr _ _ xsr ts) => [z [zr [xz tz]]].
by rewrite - (ma _ xz).
Qed.

Theorem left_directed_mimimal: forall r x,
  left_directed r -> minimal_element r x -> least_element r x.
Proof.
move=> r x rr [xsr ma]; split=>//.
move=> t ts.
move: rr;rewrite left_directed_pr; move => [or rr].
move:(rr _ _ xsr ts) => [z [zr [xz tz]]].
by rewrite - (ma _ xz).
Qed.

Lattices


Definition lattice r := order r &
  forall x, forall y, inc x (substrate r) -> inc y (substrate r) ->
    (has_supremum r (doubleton x y) & has_infimum r (doubleton x y)).

Lemma lattice_sup_pr: forall r a b,
  lattice r -> inc a (substrate r) -> inc b (substrate r) ->
  (gle r a (sup r a b) & gle r b (sup r a b) &
    forall z, gle r a z -> gle r b z -> gle r (sup r a b) z).
Proof.
move=> r a b [or lr] asr bsr.
by move: (lr _ _ asr bsr)=> [p1 p2]; apply sup_pr.
Qed.

Lemma lattice_inf_pr: forall r a b,
  lattice r -> inc a (substrate r) -> inc b (substrate r) ->
  (gle r (inf r a b) a & gle r (inf r a b) b&
    forall z, gle r z a -> gle r z b -> gle r z (inf r a b)).
Proof.
move=> r a b [or lr] asr bsr.
by move: (lr _ _ asr bsr)=> [p1 p2]; apply inf_pr.
Qed.

Lemma inf_inclusion: forall A x y, sub x A -> sub y A ->
  greatest_lower_bound (inclusion_order A) (doubleton x y) (intersection2 x y).
Proof.
move=> A x y xA yA.
have sd: (sub (doubleton x y) (powerset A)) by apply sub_doubleton; aw.
move: (intersection_is_inf sd).
rewrite Y_if_rw //; apply nonempty_doubleton.
Qed.

Lemma sup_inclusion: forall A x y, sub x A -> sub y A ->
  least_upper_bound (inclusion_order A) (doubleton x y) (union2 x y).
Proof.
move=> A x y xA yA.
have sd: (sub (doubleton x y) (powerset A)) by apply sub_doubleton; aw.
apply (union_is_sup sd).
Qed.

Lemma powerset_lattice: forall A, lattice (inclusion_order A).
Proof.
move=> A; split.
  apply inclusion_is_order.
rewrite substrate_inclusion_order.
move=> x y; aw=> xp yp; split.
  by exists (union2 x y); apply sup_inclusion.
by exists (intersection2 x y); apply inf_inclusion.
Qed.

Lemma product_lattice: forall g,
  product_order_axioms g ->
  (forall i, inc i (domain g) -> lattice (V i g))
  -> lattice (product_order g).
Proof.
move=>g po all; split; first by fprops.
move=> x y xs ys.
rewrite product_order_substrate // in xs ys.
have sd: sub (doubleton x y) (prod_of_substrates g).
  by move=> t td; case (doubleton_or td)=> ->.
set (f := fam_of_substrates g).
have fgf: fgraph f by rewrite /f/fam_of_substrates; gprops.
have df: domain f = domain g by rewrite /f/fam_of_substrates; bw.
have pf: productb f = prod_of_substrates g by [].
have aux: (forall i, inc i (domain g) -> exists u, exists v,
    inc u (substrate (V i g)) & inc v (substrate (V i g)) &
    (image_by_fun (pr_i f i) (doubleton x y) = doubleton u v)).
  move=> i idg; exists (V i x); exists (V i y).
  move: (xs) (ys); aw; move => [fx [dx iVx]] [fy [dy iVy]].
  split; first by apply iVx.
  split; first by apply iVy.
  have sds: sub (doubleton x y) (source (pr_i f i)).
    rewrite /pr_i bl_source.
    by move=> t td; case (doubleton_or td) => ->.
  have fi: is_function (pr_i f i) by apply pri_function=>//; ue.
  have Wx: (W x (pr_i f i) = (V i x)) by apply pri_W=>//; ue.
  have Wy: (W y (pr_i f i) = (V i y)) by apply pri_W=>//; ue.
  set_extens z; aw.
    by move=> [u [ud]] <-; case (doubleton_or ud)=> ->; ue.
  move => zd; case zd =>->.
    exists x; ee.
  by exists y;ee.
move:(sup_in_product po sd) => [h _]; rewrite -h.
move:(inf_in_product po sd) => [h' _]; rewrite -h'.
split.
  move=> i idg; move: (aux _ idg) => [u [v [us [vs]]]] -> .
  move: (all _ idg)=> [_ lt]; move: (lt _ _ us vs)=> [lt1 lt2].
  apply lt1.
move=> i idg; move: (aux _ idg) => [u [v [us [vs]]]] -> .
move: (all _ idg)=> [_ lt]; move: (lt _ _ us vs)=> [lt1 lt2].
apply lt2.
Qed.

Lemma lattice_directed: forall r,
  lattice r -> (right_directed r & left_directed r).
Proof.
move=> r [oe lr]; split; split =>// x y xs ys; move: (lr _ _ xs ys)=> [p1 p2].
  have sd: sub (doubleton x y) (substrate r) by apply sub_doubleton.
  by move: p1=> [s]; aw; move=> [ub _]; exists s.
have sd: sub (doubleton x y) (substrate r) by apply sub_doubleton.
by move: p2=> [s]; aw; move=> [ub _]; exists s.
Qed.

Totally ordered sets

Definition total_order r :=
  order r & forall x y, inc x (substrate r) -> inc y (substrate r) ->
    (gle r x y \/ gge r x y).

Lemma total_order_pr: forall r,
  total_order r =
    (compose_graph r r = r &
    intersection2 r (opposite_order r) = identity_g(substrate r)&
    union2 r (opposite_order r) = coarse (substrate r)).
Proof.
move=> r; rewrite /total_order.
apply iff_eq.
  move=> [or to]; move: (or); rewrite order_pr; move => [p1 p2].
  ee; set_extens x => xu.
   case (union2_or xu).
     have gr: (is_graph r) by fprops.
     apply (sub_graph_coarse_substrate gr).
   have oor: order (opposite_order r) by fprops.
   have sor: substrate (opposite_order r) = substrate r.
     by apply substrate_opposite_order.
   have gr: is_graph (opposite_order r) by fprops.
   by rewrite -sor; apply (sub_graph_coarse_substrate gr).
  move: xu; rewrite/coarse; aw.
  move=> [px [Ps Qs]]; rewrite -px.
  by case (to _ _ Ps Qs); [ auto | aw; auto].
move=> [cg [i2 u2]].
have or :order r by rewrite (order_pr r).
split=>//.
move=> x y xs ys.
have Jc: inc (J x y) (coarse (substrate r)).
  by rewrite /coarse;apply product_pair_inc.
move: Jc; rewrite -u2; aw.
Qed.

Lemma total_order_pr1 : forall r x y,
  total_order r -> inc x (substrate r) -> inc y (substrate r) ->
    (glt r x y \/ ggt r x y \/ x = y).
Proof.
move=> r x y tor xs ys.
case (equal_or_not x y).
  auto.
move: tor=> [_ p] xy.
rewrite /ggt/glt; case (p _ _ xs ys); auto.
Qed.

Lemma total_order_pr2 : forall r x y,
  total_order r -> inc x (substrate r) -> inc y (substrate r) ->
    (glt r x y \/ gle r y x).
Proof.
move=> r x y tor xs ys.
move: tor=> [_ p]; rewrite /glt; case (p _ _ xs ys); auto.
case (equal_or_not x y); auto.
move=> ->; auto.
Qed.

Lemma total_order_sub : forall r x,
  total_order r -> sub x (substrate r) -> total_order (induced_order r x).
Proof.
move=> r x [or tor] xs; split.
  fprops.
aw; move=> a b asr bsr; rewrite /gge; aw.
apply (tor _ _ (xs _ asr) (xs _ bsr)).
Qed.

Lemma total_order_small : forall r,
  order r -> small_set(substrate r) -> total_order r.
Proof.
move=> r or ss; split=>//.
move=> x y xs ys; rewrite (ss _ _ xs ys).
left. order_tac.
Qed.

Lemma total_order_conterexample:
  ~ (total_order (inclusion_order two_points)).
Proof.
move=> [or].
rewrite substrate_inclusion_order => to.
have sa: (inc (singleton TPa) (powerset two_points)).
  apply powerset_inc; move=> t; aw. move=> ->; fprops.
have sb: (inc (singleton TPb) (powerset two_points)).
  apply powerset_inc; move=> t; aw. move=> ->; fprops.
move: (to _ _ sa sb); rewrite /gge /gle; aw.
case; move=> [_[_ hyp]].
  have ta: (inc TPa (singleton TPa)) by fprops.
  move: (hyp _ ta); aw; apply two_points_distinct.
have tb: (inc TPb (singleton TPb)) by fprops.
move: (hyp _ tb); aw; apply two_points_distinctb.
Qed.

Lemma total_order_opposite: forall r,
  total_order r -> total_order (opposite_order r).
Proof.
move=> t [or tor].
split; first by fprops.
aw; move=> x y xs ys; case (tor _ _ xs ys); aw;auto.
Qed.

If x is smaller than y, the pair has a supremum and an infimum

Lemma sup_comparable: forall r x y, gle r x y ->
  order r -> least_upper_bound r (doubleton x y) y.
Proof.
move=> r x y xy or.
apply least_upper_bound_doubleton =>//.
order_tac; order_tac.
Qed.

Lemma inf_comparable: forall r x y, gle r x y ->
  order r -> greatest_lower_bound r (doubleton x y) x.
Proof.
move=> r x y xy or.
apply greatest_lower_bound_doubleton =>//.
order_tac; order_tac.
Qed.

Lemma inf_comparable1: forall r x y, order r -> gle r x y ->
  inf r x y = x.
Proof.
by symmetry; apply infimum_pr2 =>//; apply inf_comparable.
Qed.

Lemma sup_comparable1: forall r x y, order r -> gle r x y ->
  sup r x y = y.
Proof.
by symmetry; apply supremum_pr2 =>//; apply sup_comparable.
Qed.

Lemma total_order_lattice: forall r,
  total_order r -> lattice r.
Proof.
move=> r [or tor]; split=>//.
move=> x y xsr ysr; case (tor _ _ xsr ysr).
  move => hyp.
  split.
    by exists y; apply sup_comparable.
  by exists x; apply inf_comparable.
rewrite doubleton_symm.
move => hyp.
split.
  by exists x; apply sup_comparable.
by exists y; apply inf_comparable.
Qed.

Lemma total_order_directed: forall r,
  total_order r -> (right_directed r & left_directed r).
Proof.
by move=>r to; apply lattice_directed; apply total_order_lattice.
Qed.

Theorem total_order_monotone_injective: forall f r r',
  total_order r -> strict_monotone_fun f r r' -> injection f.
Proof.
move=> f r r' tor sm.
case sm; move=>[ff [or [or' [sr [sr' hyp]]]]]; split=>//; move=> x y xs ys sW.
  rewrite -sr in xs ys.
  case (total_order_pr1 tor xs ys) => h.
    move: (hyp _ _ h); rewrite sW /glt;intuition.
  case h=> // h'.
  have h'': glt r y x by move: h'; rewrite /glt /ggt/gge; intuition.
  move: (hyp _ _ h''); rewrite sW /glt;intuition.
rewrite -sr in xs ys.
case (total_order_pr1 tor xs ys) => h.
  move: (hyp _ _ h); rewrite sW /ggt/glt;intuition.
case h=> // h'.
have h'': glt r y x by move: h'; rewrite /glt /ggt/gge; intuition.
move: (hyp _ _ h''); rewrite sW /ggt/glt;intuition.
Qed.

Lemma increasing_fun_from_strict: forall f r r',
  strict_increasing_fun f r r' -> increasing_fun f r r'.
Proof.
move=> f r r' [ff [or [or' [sr [sr' hyp]]]]].
red; ee.
move=> x y xy; case (equal_or_not x y).
  move=> ->; order_tac.
  rewrite sr';apply inc_W_target =>//.
  rewrite -sr; order_tac.
move=> h.
have aux: (glt r x y) by split.
by case (hyp _ _ aux).
Qed.

Lemma decreasing_fun_from_strict: forall f r r',
  strict_decreasing_fun f r r' -> decreasing_fun f r r'.
Proof.

move=> f r r' [ff [or [or' [sr [sr' hyp]]]]].
red; ee.
move=> x y xy; case (equal_or_not x y).
  move=> ->; rewrite /gge;order_tac.
  rewrite sr';apply inc_W_target =>//.
  rewrite -sr; order_tac.
move=> h.
have aux: (glt r x y) by split.
by case (hyp _ _ aux).
Qed.

Theorem total_order_increasing_morphism: forall f r r',
  total_order r -> strict_increasing_fun f r r' -> order_morphism f r r'.
Proof.
move=> f r r' tor si.
have ijf: (injection f) .
  apply total_order_monotone_injective with r r'=>//.
  red; auto.
move: (increasing_fun_from_strict si) => ic.
move: si=> [ff [or [or' [sr [sr' sif]]]]].
move: ic=> [_ [_ [_ [_ [_ nif]]]]].
red; ee.
move=> x y xs ys; apply iff_eq.
  apply nif.
rewrite -sr in xs ys.
case (total_order_pr2 tor ys xs).
  move=> h; move: (sif _ _ h)=> p1 p2; order_tac.
done.
Qed.

Theorem sup_in_total_order:forall r X x, total_order r -> sub X (substrate r)->
  least_upper_bound r X x = (upper_bound r X x &
    (forall y, glt r y x -> exists z, inc z X & glt r y z & gle r z x)).
Proof.
move=> r X x tor Xs.
have or: order r by move: tor=> [].
apply iff_eq; aw; move=> [ub lub]; split=>//.
  move=> y yx.
  have nu: (~ (upper_bound r X y)).
    by move=> ys; move: (lub _ ys)=> xy; order_tac.
  have ysr: (inc y (substrate r)) by order_tac.
  have [z [zX yz]]: (exists z, inc z X & glt r y z).
    apply exists_proof; dneg nez; split =>//.
    move=> u ux; case (total_order_pr2 tor ysr (Xs _ ux)) =>//.
    by move=> aux; elim (nez u).
  by exists z; ee; move: ub=> [_]; apply.
move=> z ubz.
have xs:inc x (substrate r) by move: ub=> [].
have zs:inc z (substrate r) by move: ubz=> [].
case (total_order_pr2 tor zs xs) =>//.
move=> h; move: (lub _ h)=> [t [tX [yt tx]]].
move: ubz=> [_ h']; move: (h' _ tX) => h''.
order_tac.
Qed.

Theorem inf_in_total_order:forall r X x, total_order r -> sub X (substrate r)->
  greatest_lower_bound r X x = (lower_bound r X x &
    (forall y, glt r x y -> exists z, inc z X & glt r z y & gle r x z)).
Proof.
move=> r X x tor Xs.
have or: order r by move: tor=> [].
apply iff_eq; aw; move=> [ub lub]; split=>//.
  move=> y yx.
  have nu: (~ (lower_bound r X y)).
    by move=> ys; move: (lub _ ys)=> xy; order_tac.
  have ysr: (inc y (substrate r)) by order_tac.
  have [z [zX yz]]: (exists z, inc z X & glt r z y).
    apply exists_proof; dneg nez; split =>//.
    move=> u ux; case (total_order_pr2 tor (Xs _ ux) ysr) =>//.
    by move=> aux; elim (nez u).
  by exists z; ee; move: ub=> [_]; apply.
move=> z ubz.
have xs:inc x (substrate r) by move: ub=> [].
have zs:inc z (substrate r) by move: ubz=> [].
case (total_order_pr2 tor xs zs) =>//.
move=> h; move: (lub _ h)=> [t [tX [yt tx]]].
move: ubz=> [_ h']; move: (h' _ tX) => h''.
order_tac.
Qed.

Intervals


Definition interval_oo r a b :=Zo (substrate r)(fun z => glt r a z & glt r z b).
Definition interval_oc r a b :=Zo (substrate r)(fun z => glt r a z & gle r z b).
Definition interval_ou r a := Zo (substrate r)(fun z => glt r a z).
Definition interval_co r a b := Zo(substrate r)(fun z => gle r a z & glt r z b).
Definition interval_cc r a b := Zo(substrate r)(fun z => gle r a z & gle r z b).
Definition interval_cu r a := Zo (substrate r)(fun z => gle r a z).
Definition interval_uo r b := Zo (substrate r) (fun z => glt r z b).
Definition interval_uc r b := Zo (substrate r) (fun z => gle r z b).
Definition interval_uu r := Zo (substrate r) (fun z => True).

Definition is_closed_interval r x := exists a, exists b,
  inc a (substrate r) & inc b (substrate r) & gle r a b & x = interval_cc r a b.
Definition is_open_interval r x := exists a, exists b,
  inc a (substrate r) & inc b (substrate r) & gle r a b & x = interval_oo r a b.
Definition is_semi_open_interval r x := exists a, exists b,
  inc a (substrate r) & inc b (substrate r) & gle r a b &
  (x = interval_oc r a b \/ x = interval_co r a b).
Definition is_bounded_interval r x := is_closed_interval r x \/
  is_open_interval r x \/ is_semi_open_interval r x.
Definition is_left_unbounded_interval r x :=
  exists b, inc b (substrate r) & (x = interval_uc r b \/ x = interval_uo r b).
Definition is_right_unbounded_interval r x :=
  exists a, inc a (substrate r) & (x = interval_cu r a \/ x = interval_ou r a).
Definition is_unbounded_interval r x :=
  is_left_unbounded_interval r x \/ is_right_unbounded_interval r x \/
  x = interval_uu r.
Definition is_interval r x :=
  is_bounded_interval r x \/ is_unbounded_interval r x.

Ltac uf_interval :=
  rewrite /interval_cc/interval_oo/interval_co/interval_oc
  /interval_uu/interval_uo/interval_ou/interval_uc/interval_cu.

Lemma the_least_interval: forall r a b, order r ->
  gle r a b -> the_least_element (induced_order r (interval_cc r a b)) = a.
Proof.
move=> r a b or ab.
have sab: (sub (interval_cc r a b) (substrate r))
  by uf_interval; apply Z_sub.
have asr: inc a (substrate r) by apply (inc_arg1_substrate ab).
have aicc: inc a (interval_cc r a b).
  uf_interval; apply Z_inc =>//; split=>//; order_tac.
apply the_least_element_pr2.
  apply order_induced_order=>//.
red; uf_interval; aw.
split =>//;move=> x xi; aw; move: xi;rewrite Z_rw; intuition.
Qed.

Lemma the_greatest_interval: forall r a b, order r ->
  gle r a b -> the_greatest_element (induced_order r (interval_cc r a b)) = b.
Proof.
move=> r a b or ab.
have sab: (sub (interval_cc r a b) (substrate r))
  by uf_interval; apply Z_sub.
have asr: inc b (substrate r) by apply (inc_arg2_substrate ab).
have aicc: inc b (interval_cc r a b).
  uf_interval; apply Z_inc =>//; split=>//; order_tac.
apply the_greatest_element_pr2.
  apply order_induced_order=>//.
red; uf_interval; aw.
split =>//;move=> x xi; aw; move: xi;rewrite Z_rw; intuition.
Qed.

Lemma nonempty_closed_interval: forall r x,
  order r -> is_closed_interval r x -> nonempty x.
Proof.
move=> r x or [a [b [asr [bsr [ab xab]]]]].
exists a; rewrite xab; uf_interval; apply Z_inc =>//.
split=>//; order_tac.
Qed.

Lemma singleton_interval: forall r a,
  order r -> inc a (substrate r) -> is_singleton (interval_cc r a a).
Proof.
move=> r a or asr; exists a.
uf_interval; apply is_singleton_pr; split.
 rewrite Z_rw;ee; order_tac.
move=> z ; rewrite Z_rw;move=> [_ [p1 p2]]; order_tac.
Qed.

Lemma empty_interval: forall r a,
  order r -> inc a (substrate r) ->
  (empty (interval_co r a a) & empty (interval_oc r a a)&
    empty (interval_oo r a a)).
Proof.
move=> r a or asr; uf_interval.
ee; move=> y;rewrite Z_rw; move=> [_ [p1 p2]]; order_tac.
Qed.

Lemma lattice_inverse: forall r, lattice r -> lattice (opposite_order r).
Proof.
move=> r [or lr]; split; first by fprops.
aw. move=> x y xs ys.
move: (lr _ _ xs ys) => [[x1 p1] [x2 p2]].
split.
  exists x2; rewrite - inf_sup_opp //; apply sub_doubleton =>//.
exists x1; rewrite - sup_inf_opp //; apply sub_doubleton =>//.
Qed.

Lemma intersection4: forall A B C D,
  intersection2 (intersection2 A B) (intersection2 C D)
  = intersection2 (intersection2 A C) (intersection2 B D).
Proof.
move=> A B C D.
rewrite - 2! intersection2A.
apply f_equal.
rewrite 2! intersection2A.
by rewrite (intersection2C B C).
Qed.

Lemma intersection_i1: forall r x,
  is_interval r x -> intersection2 x (interval_uu r) = x.
Proof.
move=> r x hyp.
have ->: interval_uu r = substrate r.
  rewrite /interval_uu; apply extensionality; first by apply Z_sub.
  by move=> t tr; rewrite Z_rw.
suff: sub x (substrate r).
  move=> h; apply extensionality; first by apply intersection2sub_first.
  by move=> t tx; apply intersection2_inc=>//; apply h.
case hyp.
  case; first by move=> [a [b [_[_[_]]]]] ->; uf_interval ; apply Z_sub.
  case.
    move=> [a [b [_[_[_]]]]] ->; uf_interval ; apply Z_sub.
  move=> [a [b [_[_[_]]]]].
  uf_interval; case; move=> ->; apply Z_sub.
case.
  move=> [a [_]]; case; move=> ->; apply Z_sub.
case.
  move=> [a [_]]; case; move=> ->; apply Z_sub.
move=> ->; apply Z_sub.
Qed.

Definition is_lu_interval r x :=
   x = interval_uu r \/ is_left_unbounded_interval r x.
Definition is_ru_interval r x :=
   x= interval_uu r \/ is_right_unbounded_interval r x.

Ltac zztac2 v := uf_interval; set_extens v ; aw; rewrite ? Z_rw; aw.

Lemma intersection_i2: forall r x,
  is_interval r x ->
  (exists u, exists v, is_lu_interval r u & is_ru_interval r v &
    intersection2 u v = x).
Proof.
move=> r x ix; move: (ix).
have p1: forall b, inc b (substrate r) -> is_lu_interval r (interval_uc r b).
  move=> b hb; right; exists b;auto.
have p2: forall b, inc b (substrate r) -> is_lu_interval r (interval_uo r b).
  move=> b hb; right; exists b; auto.
have p3: forall b, inc b (substrate r) -> is_ru_interval r (interval_cu r b).
  move=> b hb; right; exists b; auto.
have p4: forall b, inc b (substrate r) -> is_ru_interval r (interval_ou r b).
  move=> b hb; right; exists b; auto.
case.
  case.
    move=> [a [b [au [bu [ab xab]]]]].
    exists (interval_uc r b); exists (interval_cu r a); ee.
    rewrite xab; zztac2 y; intuition.
  case.
    move=> [a [b [au [bu [ab xab]]]]].
    exists (interval_uo r b); exists (interval_ou r a); ee.
    rewrite xab; zztac2 y; intuition.
  move=> [a [b [au [bu [ab xab]]]]].
  case xab=> ->.
    exists (interval_uc r b); exists (interval_ou r a); ee.
    zztac2 y; intuition.
  exists (interval_uo r b); exists (interval_cu r a); ee.
  zztac2 y; intuition.
case.
  move=>[a [au xa]].
  exists x; exists (interval_uu r).
  split. (case xa); move=> ->; intuition.
  by split=>//; [red; auto | apply intersection_i1].
case.
  move=>[a [au xa]].
  exists (interval_uu r); exists x.
  split; first by red; auto.
  split.
     (case xa); move=> ->; intuition.
  by rewrite intersection2C; apply intersection_i1.
move=> h; exists x; exists x; rewrite h.
split; first by red; auto.
split; first by red; auto.
apply intersection_i1. ue.
Qed.

Lemma intersection_i3: forall r x y, lattice r ->
  is_left_unbounded_interval r x -> is_left_unbounded_interval r y ->
  is_left_unbounded_interval r (intersection2 x y).
Proof.
move=> r x y lar [a [asr xa]][b [bsr yb]].
have or: order r by move: lar=> [].
move: (lattice_inf_pr lar asr bsr).
set (u:=inf r a b); set (v:= sup r a b).
move=> [ua [ub uab]].
exists u; split; first by order_tac.
case xa => ->.
  case yb => ->.
    left; zztac2 z; first by intuition.
    move=> [zs zu]; ee; order_tac.
  case (p_or_not_p (glt r u b)).
  left; zztac2 z.
      move=> [[zs za][_ [zb znb]]]; ee.
    move=> [zs zu]; ee; order_tac.
  move=> ltuv; right; zztac2 z.
    move=> [[zs za][_ [zb nzb]]]; split =>//.
    split; first by apply uab.
    move=> zu; rewrite zu in zb; elim ltuv; split=>//; ue.
  move=> [zs zu]; ee; [ move: zu=> [zu _]; order_tac | order_tac ].
case yb=> ->.
  case (p_or_not_p (glt r u a)).
  left; zztac2 z.
      move=> [[zs [za zna]][_ zb]]; ee.
    move=> [zs zu]; ee; order_tac.
  move=> ltuv; right; zztac2 z.
    move=> [[zs [za nza]][_ zb]]; split =>//.
    split; first by apply uab.
    move=> zu; rewrite zu in zb; elim ltuv; split=>//; ue.
  move=> [zs zu]; ee; [order_tac | move: zu=> [zu _]; order_tac ].
case (p_or_not_p (glt r u a & glt r u b)).
  move=> [p1 p2].
  left; zztac2 z.
    move=> [[zs [za zna]][_ [zb znb]]]; ee.
   move=> [zs zu]; ee; order_tac.
move=> p1p2; right; zztac2 z.
  move=> [[zs [za zna]][_ [zb znb]]]; rewrite /glt; ee.
  move=> zu; rewrite zu in zna znb; elim p1p2; split; split;ee.
move=> [zs zu]; ee; order_tac.
Qed.

Theorem intersection_interval: forall r x y,
  lattice r -> is_interval r x -> is_interval r y ->
  is_interval r (intersection2 x y).
Proof.
move=> r x y lar ix iy.
have or: order r by move: lar=> [].
move: (intersection_i2 ix) (intersection_i2 iy).
move=> [a [b [lua [rub abx]]]][c [d [luc [rud cdy]]]].
rewrite -abx -cdy intersection4.
have lu1: is_lu_interval r (intersection2 a c).
  case lua.
     move=>->; rewrite intersection2C intersection_i1 //.
     right; red; red in luc; intuition.
  case luc.
    move=>-> p; rewrite intersection_i1;[ by right| by right; left].
  by move=> p1 p2; right; apply intersection_i3.
have ru1: is_ru_interval r (intersection2 b d).
  case rub.
     move=>->; rewrite intersection2C intersection_i1 //.
     right; red; red in rud; intuition.
  move=> irub; case rud.
     move=>->; rewrite intersection_i1 //; by right; right; left.
  move=> irud.
  have Ha: forall a, interval_cu r a = interval_uc (opposite_order r) a.
   uf_interval; move=> t; aw; set_extens u; rewrite ? Z_rw; aw.
  have Hb: forall a, interval_ou r a = interval_uo (opposite_order r) a.
    uf_interval; move=> t; aw; set_extens u; rewrite ? Z_rw /glt; aw; ee.
  have Hc: is_left_unbounded_interval (opposite_order r) b.
    move:irub => [e [es be]]; case be => ->; exists e; split; aw; auto.
  have Hd: is_left_unbounded_interval (opposite_order r) d.
    move:irud => [e [es be]]; case be => ->; exists e; split; aw; auto.
  have He :lattice (opposite_order r) by apply lattice_inverse.
  move: (intersection_i3 He Hc Hd) => [e [es]].
  rewrite -Ha -Hb; awi es =>//; case => ->; right; exists e; auto.
have i1: is_interval r (intersection2 a c).
  right; red; red in lu1; intuition.
have i2: is_interval r (intersection2 b d).
  right; red; red in ru1; intuition.
case lu1.
  move=>->. rewrite intersection2C intersection_i1 //.
move=> lu2; case ru1; first by move=>->;rewrite intersection_i1 =>//.
 move=> ru2.
left.
move: lu2 ru2 => [e [es ace]][f [fs bdf]].
case (p_or_not_p (gle r f e)) => fe.
  case ace=> ->.
    case bdf=> ->.
      left; exists f; exists e; ee; zztac2 t; intuition.
    right;right;exists f; exists e; ee; left; zztac2 t; intuition.
  case bdf=> ->.
    right;right;exists f; exists e; ee; right; zztac2 t; intuition.
  right; left ;exists f; exists e; ee; zztac2 t; intuition.
suff: (empty (intersection2 (intersection2 a c) (intersection2 b d))).
  move=> h.
  move:(empty_interval or fs) => [_[ _ oo]]; right; left.
  exists f; exists f; ee; try order_tac.
  by set_extens t => te; [ elim (h t) | elim (oo t)].
move=> t; rewrite intersection2_rw; move => [ti1 ti2]; elim fe.
have p1: gle r t e.
 by move: ti1; case ace=> ->; uf_interval; rewrite Z_rw /glt; intuition.
have p2: gle r f t.
 by move: ti2; case bdf=> ->; uf_interval; rewrite Z_rw /glt; intuition.
order_tac.
Qed.

End Border.
Export Border.