/- Port of `Lattice.agda`. Most of the Agda module is *lifted* into mathlib, since we now work with propositional equality instead of a setoid: IsSemilattice A _≈_ _⊔_ ↦ SemilatticeSup α IsLattice A _≈_ _⊔_ _⊓_ ↦ Lattice α _≼_ (a ⊔ b ≈ b) ↦ a ≤ b (bridge: `sup_eq_right`) _≺_ ↦ a < b Monotonic ↦ Monotone ⊔-assoc/⊔-comm/⊔-idemp ↦ sup_assoc/sup_comm/sup_idem absorb-⊔-⊓/absorb-⊓-⊔ ↦ sup_inf_self/inf_sup_self ≼-refl/≼-trans/≼-antisym ↦ le_refl/le_trans/le_antisymm x≼x⊔y ↦ le_sup_left ⊔-Monotonicˡ/ʳ ↦ sup_le_sup_left/sup_le_sup_right id-Mono/const-Mono ↦ monotone_id/monotone_const IsDecidable ↦ DecidableEq (kept only where computation needs it) Chain (Chain.agda) ↦ LTSeries (chains of `<`); concat ↦ RelSeries.smash ChainMapping.Chain-map ↦ LTSeries.map (Monotone + Injective ⇒ StrictMono) What remains custom is exactly what mathlib does not have: * monotonicity of folds over pairwise-related lists (foldr-Mono & friends), * the fixed-height machinery (Chain.Height ↦ FixedHeight, Bounded), * the proof that the bottom of the longest chain is a least element (⊥≼). -/ import Mathlib.Order.Lattice import Mathlib.Order.RelSeries namespace Spa /-! ### Monotonicity helpers (Lattice.agda, `Monotonic₂` and fold lemmas) -/ /-- Agda: `Monotonic₂` (a pair of one-sided monotonicity proofs). -/ def Monotone₂ {α β γ : Type*} [Preorder α] [Preorder β] [Preorder γ] (f : α → β → γ) : Prop := (∀ b, Monotone fun a => f a b) ∧ (∀ a, Monotone (f a)) section Folds variable {α β : Type*} [Preorder α] [Preorder β] /-- Agda: `foldr-Mono`. `Pairwise _≼₁_` becomes `List.Forall₂ (· ≤ ·)`. -/ theorem foldr_mono {l₁ l₂ : List α} (f : α → β → β) {b₁ b₂ : β} (hl : List.Forall₂ (· ≤ ·) l₁ l₂) (hb : b₁ ≤ b₂) (hf₁ : ∀ b, Monotone fun a => f a b) (hf₂ : ∀ a, Monotone (f a)) : l₁.foldr f b₁ ≤ l₂.foldr f b₂ := by induction hl with | nil => exact hb | cons hxy _ ih => exact le_trans (hf₁ _ hxy) (hf₂ _ ih) /-- Agda: `foldl-Mono`. -/ theorem foldl_mono {l₁ l₂ : List α} (f : β → α → β) {b₁ b₂ : β} (hl : List.Forall₂ (· ≤ ·) l₁ l₂) (hb : b₁ ≤ b₂) (hf₁ : ∀ a, Monotone fun b => f b a) (hf₂ : ∀ b, Monotone (f b)) : l₁.foldl f b₁ ≤ l₂.foldl f b₂ := by induction hl generalizing b₁ b₂ with | nil => exact hb | cons hxy _ ih => exact ih (le_trans (hf₁ _ hb) (hf₂ _ hxy)) omit [Preorder α] in /-- Agda: `foldr-Mono'` (fixed list, varying accumulator). -/ theorem foldr_mono' (l : List α) (f : α → β → β) (hf : ∀ a, Monotone (f a)) : Monotone fun b => l.foldr f b := by intro b₁ b₂ hb induction l with | nil => exact hb | cons x xs ih => exact hf x ih omit [Preorder α] in /-- Agda: `foldl-Mono'`. -/ theorem foldl_mono' (l : List α) (f : β → α → β) (hf : ∀ a, Monotone fun b => f b a) : Monotone fun b => l.foldl f b := by intro b₁ b₂ hb induction l generalizing b₁ b₂ with | nil => exact hb | cons x xs ih => exact ih (hf x hb) end Folds /-! ### Fixed height (Chain.agda `Bounded`/`Height`, Lattice.agda `FixedHeight`) -/ /-- Agda: `Chain.Bounded`. Every `<`-chain has length at most `n`. -/ def BoundedChains (α : Type*) [Preorder α] (n : ℕ) : Prop := ∀ c : LTSeries α, c.length ≤ n /-- Agda: `Chain.Height` (with `FixedHeight h = Height h` from Lattice.agda). A longest chain runs from `⊥` to `⊤` and has length exactly `height`; no chain is longer. -/ structure FixedHeight (α : Type*) [Preorder α] (height : ℕ) where bot : α top : α longestChain : LTSeries α head_longestChain : longestChain.head = bot last_longestChain : longestChain.last = top length_longestChain : longestChain.length = height bounded : BoundedChains α height /-- Agda: `Chain.Bounded-suc-n` (a bounded order admits no chain one longer). -/ theorem BoundedChains.no_longer {α : Type*} [Preorder α] {n : ℕ} (h : BoundedChains α n) (c : LTSeries α) : c.length ≠ n + 1 := fun hc => absurd (h c) (by omega) /-- Re-index a `FixedHeight` along an equality of heights (used where Agda just rewrites with arithmetic identities). -/ def FixedHeight.cast {α : Type*} [Preorder α] {m n : ℕ} (h : m = n) (fh : FixedHeight α m) : FixedHeight α n where bot := fh.bot top := fh.top longestChain := fh.longestChain head_longestChain := fh.head_longestChain last_longestChain := fh.last_longestChain length_longestChain := h ▸ fh.length_longestChain bounded := h ▸ fh.bounded @[simp] theorem FixedHeight.cast_bot {α : Type*} [Preorder α] {m n : ℕ} (h : m = n) (fh : FixedHeight α m) : (fh.cast h).bot = fh.bot := rfl /-- Agda: `IsFiniteHeightLattice` / `FiniteHeightLattice` (bundled). Like the Agda code (which took `IsFiniteHeightLattice` as an instance argument `⦃·⦄`), this is a typeclass; downstream modules pick it up by instance resolution rather than threading a `FixedHeight` value. -/ class FiniteHeightLattice (α : Type*) [Lattice α] where height : ℕ fixedHeight : FixedHeight α height namespace FixedHeight variable {α : Type*} [Lattice α] {h : ℕ} /-- Agda: `Known-⊥`. -/ def KnownBot (fh : FixedHeight α h) : Prop := ∀ a : α, fh.bot ≤ a /-- Agda: `Known-⊤`. -/ def KnownTop (fh : FixedHeight α h) : Prop := ∀ a : α, a ≤ fh.top /-- Agda: `⊥≼` — the bottom of the longest chain is a least element. Same proof: if `⊥ ⊓ a ≠ ⊥` then `⊥ ⊓ a < ⊥` prepends to the longest chain, contradicting boundedness. (The decidability hypothesis of the Agda proof is not needed classically.) -/ theorem bot_le (fh : FixedHeight α h) : fh.KnownBot := by intro a by_cases heq : fh.bot ⊓ a = fh.bot · exact inf_eq_left.mp heq · exfalso have hlt : fh.bot ⊓ a < fh.bot := lt_of_le_of_ne inf_le_left heq exact fh.bounded.no_longer (fh.longestChain.cons (fh.bot ⊓ a) (fh.head_longestChain ▸ hlt)) (by simp [RelSeries.cons, fh.length_longestChain]) end FixedHeight namespace FiniteHeightLattice variable (α : Type*) [Lattice α] [FiniteHeightLattice α] /-- Agda: the `⊥` of `Chain.Height`, with the type explicit. -/ def bot : α := (fixedHeight (α := α)).bot /-- Agda: `⊥≼` for the instance bottom. -/ theorem bot_le (a : α) : bot α ≤ a := FixedHeight.bot_le _ a end FiniteHeightLattice end Spa