A lattice is the combination of a join and meet semi-lattice.
Require Import base order adjunction jlat mlat.

Module Lat.
  Section ClassDef.
    Record class_of (T : Type) := Class {
      base : Ord.class_of T;
      jmixin : JLat.mixin_of (Ord.Pack base T);
      mmixin : MLat.mixin_of (Ord.Pack base T)
    Local Coercion base : class_of >-> Ord.class_of.

    Definition jmixin_of T (c : class_of T) : JLat.class_of T :=
      @JLat.Class T (base c) (jmixin c).
    Local Coercion jmixin_of : class_of >-> JLat.class_of.

    Definition mmixin_of T (c : class_of T) : MLat.class_of T :=
      @MLat.Class T (base c) (mmixin c).
    Local Coercion mmixin_of : class_of >-> MLat.class_of.

    Structure type := Pack {sort; _ : class_of sort; _ : Type}.
    Local Coercion sort : type >-> Sortclass.

    Variables (T : Type) (cT : type).
    Definition class := let: Pack _ c _ as cT' := cT return class_of cT' in c.
    Definition clone c of phant_id class c := @Pack T c T.
    Let xT := let: Pack T _ _ := cT in T.
    Notation xclass := (class : class_of xT).

    Definition pack :=
      fun (b : ordType) & phant_id (Ord.sort b) T =>
      fun (bc : Ord.class_of _) & phant_id (Ord.class b) bc =>
      fun (jm : JLat.mixin_of (@Ord.Pack T bc T)) =>
      fun (mm : MLat.mixin_of (@Ord.Pack T bc T)) => Pack (@Class T bc jm mm) T.

    Definition proType := @Pro.Pack cT xclass xT.
    Definition ordType := @Ord.Pack cT xclass xT.
    Definition jlat := @JLat.Pack cT xclass xT.
    Definition mlat := @MLat.Pack cT xclass xT.
  End ClassDef.

  Module Exports.
    Coercion base : class_of >-> Ord.class_of.
    Coercion sort : type >-> Sortclass.

    Coercion proType : type >-> Pro.type.
    Canonical proType.

    Coercion ordType : type >-> Ord.type.
    Canonical ordType.

    Coercion jlat : type >-> JLat.type.
    Canonical jlat.

    Coercion mlat : type >-> MLat.type.
    Canonical mlat.

    Coercion jmixin_of : class_of >-> JLat.class_of.
    Coercion mmixin_of : class_of >-> MLat.class_of.

    Notation lat := type.
    Notation Lat T j m := (@pack T _ id _ id j m).
    Notation "[ 'lat' 'of' T ]" := (@clone T _ _ id)
      (at level 0, format "[ 'lat' 'of' T ]") : form_scope.
  End Exports.
End Lat.
Export Lat.Exports.

Section LatticeLaws.
  Variable (X : lat).
  Implicit Types (x y z : X).

  Lemma meetxK x y : x \cap (y \cup x) = x.
  Proof. apply: le_eq. exact: meetEl. apply: meetI => //. exact: joinIr. Qed.

  Lemma meetKx x y : x \cap (x \cup y) = x.
  Proof. rewrite joinC. exact: meetxK. Qed.

  Lemma joinxK x y : x \cup (y \cap x) = x.
  Proof. apply: le_eq. apply: joinE => //. exact: meetEr. exact: joinIl. Qed.

  Lemma joinKx x y : x \cup (x \cap y) = x.
  Proof. rewrite meetC. exact: joinxK. Qed.

  Lemma joinTx x : (top : X) \cup x = top.
  Proof. apply: top_eq. exact: joinIl. Qed.

  Lemma joinxT x : x \cup top = top.
  Proof. by rewrite joinC joinTx. Qed.

  Lemma meetBx x : (bot : X) \cap x = bot.
  Proof. apply: bot_eq. exact: meetEl. Qed.

  Lemma meetxB x : x \cap bot = bot.
  Proof. by rewrite meetC meetBx. Qed.

  (* the one direction of distributivity which holds in an arbitrary lattice *)
  Lemma join_dist_le x y z :
    x \cup (y \cap z) <= (x \cup y) \cap (x \cup z).
    apply: joinP. apply: meetP; exact: joinL.
    apply: meetP. apply: meetL. exact: joinR. apply: meetR. exact: joinR.

End LatticeLaws.

Example mono_anti_const {X : Lat} {Y : ordType} (f : {mono X -> Y}) :
  antitone f -> forall x y, f x = f y.
  move=> af. suff: forall x y, f x <= f y. move=> h x y. apply: le_eq; exact: h.
  move=> x y. rewrite (@join_left _ x y) -{2}(@meet_right _ x y). apply: af.
  rewrite rev_leE. apply: joinL. exact: meetL.


Canonical reverse_lat (T : lat) :=
  Eval hnf in Lat T^r (reverse_jlatMixin T) (reverse_mlatMixin T).

Canonical prop_lat := Eval hnf in Lat Prop prop_jlatMixin prop_mlatMixin.

Canonical pair_lat (X Y : lat) :=
  Eval hnf in Lat (X * Y) (pair_jlatMixin X Y) (pair_mlatMixin X Y).

Canonical iprod_lat (T : Type) (F : T -> lat) :=
  Eval hnf in Lat (iprod F) (iprod_jlatMixin F) (iprod_mlatMixin F).

Canonical prod_lat (T : Type) (X : lat) :=
  Eval hnf in Lat (T -> X) (@iprod_jlatMixin T (fun _ => X)) (@iprod_mlatMixin T (fun _ => X)).

Canonical mfun_lat (X : proType) (Y : lat) :=
  Eval hnf in Lat {mono X -> Y} (mfun_jlatMixin X Y) (mfun_mlatMixin X Y).

Semilattice of adjunctions between lattices.

Instance is_adjunction_bot {X Y : lat} : is_adjunction (bot : X -> Y) (top : Y -> X).
Proof. move=> x y. split=> h. exact: topI. exact: botE. Qed.

Instance is_adjunction_join {X Y : lat} (f g : X -> Y) {fr gr : Y -> X}
         {fP : is_adjunction f fr} {gP : is_adjunction g gr} :
  is_adjunction (f \cup g) (fr \cap gr).
  move=> x y. split=> [<-|->].
  - apply: meetI; rewrite -adjP. exact: joinIl. exact: joinIr.
  - apply: joinE; rewrite adjP. exact: meetEl. exact: meetEr.

Section AdjunctionJLat.
  Variables (X Y : lat).
  Implicit Types (f g : {adj X -> Y}).

  Definition adj_bot : {adj X -> Y} := adj bot.
  Definition adj_join f g : {adj X -> Y} := adj ((f : X -> Y) \cup g).

  Lemma adj_le_bot f : adj_bot <= f.
  Proof. move => x /=. exact: botE. Qed.

  Lemma adj_le_join f g : is_lub (pairb f g) (adj_join f g).
    apply: mk_join. exact: joinIl. exact: joinIr. move=> /= h l1 l2. exact: joinE.

  Canonical adj_jlatMixin := JLatMixin adj_le_bot adj_le_join.
  Canonical adj_jlat := Eval hnf in JLat {adj X -> Y} adj_jlatMixin.
End AdjunctionJLat.