@@ -0,0 +1,666 @@
/-
Port of `Lattice / FiniteMap.agda` (and the parts of `Lattice / Map.agda` it was
built on).
Representation change enabled by dropping the setoid: a finite map over a
*fixed* key list `ks` is an association list whose key spine is *exactly* `ks`:
FiniteMap A B ks := { l : List (A × B) / / l.map Prod.fst = ks }
Since the spine (including order) is pinned by the type, the representation is
canonical and propositional equality coincides with the Agda `_≈_` (pointwise
value equality). The 1100 - line `Lattice / Map.agda` — whose unordered - keys
union / intersection and `Provenance` machinery existed to make `_≈_` workable —
collapses into the positional `combine` below.
Correspondence (Agda ↦ Lean):
FiniteMap, _≈_, ≈ - Decidable ↦ FiniteMap, `=`, DecidableEq instance
_⊔_ / _⊓_ (via Map union / inter) ↦ Max / Min via `combine`
isUnionSemilattice,
isIntersectSemilattice,
isLattice, lattice ↦ instance Lattice (FiniteMap A B ks) (Lattice.mk',
i.e. the same "two semilattices + absorption" data)
_∈_, _∈k_, ∈k - dec, forget ↦ Membership instance, MemKey (+ Decidable),
mem_key_of_mem
locate ↦ locate (computable)
all - equal - keys ↦ spine_eq
∈k - exclusive ↦ immediate from memKey_iff (both sides ↔ k ∈ ks)
m₁≼m₂⇒m₁[k]≼m₂[k] ↦ le_of_mem_mem (takes `ks.Nodup`; the Agda Map
carried key - uniqueness intrinsically)
m₁≈m₂⇒k∈m₁⇒k∈km₂⇒v₁≈v₂ ↦ trivial with `=` (congruence)
_updating_via_ + Map lemmas:
updating - via - keys - ≡ ↦ (the `property` field of `updating`)
updating - via - ∈k - forward ↦ memKey_updating
updating - via - k∈ks ↦ mem_updating
updating - via - k∈ks - ≡ ↦ eq_of_mem_updating
updating - via - k∉ks - forward ↦ mem_updating_of_not_mem
updating - via - k∉ks - backward ↦ mem_of_mem_updating
f' - Monotonic (Map) ↦ updating_mono
GeneralizedUpdate:
f' ↦ generalizedUpdate
f' - Monotonic ↦ generalizedUpdate_monotone
f' - ∈k - forward ↦ generalizedUpdate_memKey
f' - k∈ks ↦ generalizedUpdate_mem
f' - k∈ks - ≡ ↦ generalizedUpdate_mem_eq
f' - k∉ks - forward, - backward ↦ generalizedUpdate_not_mem_forward, _backward
_[_], [] - ∈ ↦ valuesAt, mem_valuesAt (takes `ks.Nodup`)
m₁≼m₂⇒m₁[ks]≼m₂[ks] ↦ valuesAt_le
Provenance - union ↦ mem_sup
⊔ - combines ↦ (omitted: only used inside the Agda
isomorphism proofs, which simplified away)
IterProdIsomorphism.from / to ↦ toIter / ofIter — no `Unique ks` needed: the
spine - pinned representation is already
canonical, so the isomorphism is exact
from / to - preserves - ≈, - ⊔ - distr ↦ toIter_monotone / ofIter_monotone (with `≼`
being `≤`, the transport interface consumes
monotonicity directly)
from - to - inverseˡ / ʳ ↦ toIter_ofIter / ofIter_toIter
to - build ↦ mem_ofIter_build
FixedHeight.fixedHeight ↦ FiniteMap.fixedHeight (still obtained by
transport along the IterProd isomorphism)
⊥ - contains - bottoms ↦ bot_contains_bots
-/
import Spa . Lattice . IterProd
import Spa . Isomorphism
namespace Spa
/-- Agda: `FiniteMap = Σ Map (λ m → Map.keys m ≡ ks)`. -/
def FiniteMap ( A B : Type * ) ( ks : List A ) : Type _ : =
{ l : List ( A × B ) / / l . map Prod . fst = ks }
namespace FiniteMap
variable { A B : Type * } { ks : List A }
instance [ DecidableEq A ] [ DecidableEq B ] : DecidableEq ( FiniteMap A B ks ) : =
fun a b = > decidable_of_iff ( a . val = b . val ) Subtype . ext_iff . symm
/-- Agda: `all - equal - keys`. -/
theorem spine_eq ( fm₁ fm₂ : FiniteMap A B ks ) :
fm₁ . val . map Prod . fst = fm₂ . val . map Prod . fst : =
fm₁ . property . trans fm₂ . property . symm
/- ! ### The lattice structure (`combine` replaces Map union / intersection) -/
/-- Positional combination of two maps with equal spines. -/
def combine ( f : B → B → B ) ( l₁ l₂ : List ( A × B ) ) : List ( A × B ) : =
List . zipWith ( fun p q = > ( p . 1 , f p . 2 q . 2 ) ) l₁ l₂
theorem combine_spine ( f : B → B → B ) : ∀ { l₁ l₂ : List ( A × B ) } ,
l₁ . map Prod . fst = l₂ . map Prod . fst →
( combine f l₁ l₂ ) . map Prod . fst = l₁ . map Prod . fst
| [ ] , [ ] , _ = > rfl
| p :: l₁ , q :: l₂ , h = > by
simp only [ List . map_cons , List . cons . injEq ] at h
simp only [ combine , List . zipWith_cons_cons , List . map_cons ]
exact congrArg _ ( combine_spine f h . 2 )
| [ ] , _ :: _ , h = > by simp at h
| _ :: _ , [ ] , h = > by simp at h
theorem combine_comm ( f : B → B → B ) ( hf : ∀ a b , f a b = f b a ) :
∀ { l₁ l₂ : List ( A × B ) } , l₁ . map Prod . fst = l₂ . map Prod . fst →
combine f l₁ l₂ = combine f l₂ l₁
| [ ] , [ ] , _ = > rfl
| p :: l₁ , q :: l₂ , h = > by
simp only [ List . map_cons , List . cons . injEq ] at h
simp only [ combine , List . zipWith_cons_cons ]
rw [ h . 1 , hf ]
exact congrArg _ ( combine_comm f hf h . 2 )
| [ ] , _ :: _ , h = > by simp at h
| _ :: _ , [ ] , h = > by simp at h
theorem combine_assoc ( f : B → B → B ) ( hf : ∀ a b c , f ( f a b ) c = f a ( f b c ) ) :
∀ { l₁ l₂ l₃ : List ( A × B ) } ,
l₁ . map Prod . fst = l₂ . map Prod . fst → l₂ . map Prod . fst = l₃ . map Prod . fst →
combine f ( combine f l₁ l₂ ) l₃ = combine f l₁ ( combine f l₂ l₃ )
| [ ] , [ ] , [ ] , _ , _ = > rfl
| p :: l₁ , q :: l₂ , r :: l₃ , h₁₂ , h₂₃ = > by
simp only [ List . map_cons , List . cons . injEq ] at h₁₂ h₂₃
simp only [ combine , List . zipWith_cons_cons ]
rw [ hf ]
exact congrArg _ ( combine_assoc f hf h₁₂ . 2 h₂₃ . 2 )
| [ ] , [ ] , _ :: _ , _ , h = > by simp at h
| [ ] , _ :: _ , _ , h , _ = > by simp at h
| _ :: _ , [ ] , _ , h , _ = > by simp at h
| _ :: _ , _ :: _ , [ ] , _ , h = > by simp at h
theorem combine_absorb ( f g : B → B → B ) ( hfg : ∀ a b , f a ( g a b ) = a ) :
∀ { l₁ l₂ : List ( A × B ) } , l₁ . map Prod . fst = l₂ . map Prod . fst →
combine f l₁ ( combine g l₁ l₂ ) = l₁
| [ ] , [ ] , _ = > rfl
| p :: l₁ , q :: l₂ , h = > by
simp only [ List . map_cons , List . cons . injEq ] at h
simp only [ combine , List . zipWith_cons_cons , hfg ]
exact congrArg _ ( combine_absorb f g hfg h . 2 )
| [ ] , _ :: _ , h = > by simp at h
| _ :: _ , [ ] , h = > by simp at h
variable [ Lattice B ]
instance : Max ( FiniteMap A B ks ) where
max fm₁ fm₂ : =
⟨ combine ( · ⊔ · ) fm₁ . val fm₂ . val ,
( combine_spine _ ( spine_eq fm₁ fm₂ ) ) . trans fm₁ . property ⟩
instance : Min ( FiniteMap A B ks ) where
min fm₁ fm₂ : =
⟨ combine ( · ⊓ · ) fm₁ . val fm₂ . val ,
( combine_spine _ ( spine_eq fm₁ fm₂ ) ) . trans fm₁ . property ⟩
@[ simp ] theorem sup_val ( fm₁ fm₂ : FiniteMap A B ks ) :
( fm₁ ⊔ fm₂ ) . val = combine ( · ⊔ · ) fm₁ . val fm₂ . val : = rfl
@[ simp ] theorem inf_val ( fm₁ fm₂ : FiniteMap A B ks ) :
( fm₁ ⊓ fm₂ ) . val = combine ( · ⊓ · ) fm₁ . val fm₂ . val : = rfl
/-- Agda: `isLattice` / `lattice` (built like the Agda record from the two
semilattices plus absorption; `Lattice.mk'` defines `a ≤ b ↔ a ⊔ b = b`). -/
instance : Lattice ( FiniteMap A B ks ) : =
Lattice . mk'
( fun a b = > Subtype . ext ( combine_comm _ sup_comm ( spine_eq a b ) ) )
( fun a b c = > Subtype . ext ( combine_assoc _ sup_assoc ( spine_eq a b ) ( spine_eq b c ) ) )
( fun a b = > Subtype . ext ( combine_comm _ inf_comm ( spine_eq a b ) ) )
( fun a b c = > Subtype . ext ( combine_assoc _ inf_assoc ( spine_eq a b ) ( spine_eq b c ) ) )
( fun a b = > Subtype . ext ( combine_absorb _ _ ( fun _ _ = > sup_inf_self ) ( spine_eq a b ) ) )
( fun a b = > Subtype . ext ( combine_absorb _ _ ( fun _ _ = > inf_sup_self ) ( spine_eq a b ) ) )
/- ! ### Membership -/
instance : Membership ( A × B ) ( FiniteMap A B ks ) : =
⟨ fun fm p = > p ∈ fm . val ⟩
omit [ Lattice B ] in
theorem mem_def { p : A × B } { fm : FiniteMap A B ks } : p ∈ fm ↔ p ∈ fm . val : =
Iff . rfl
/-- Agda: `_∈k_`. -/
def MemKey ( k : A ) ( fm : FiniteMap A B ks ) : Prop : =
k ∈ fm . val . map Prod . fst
omit [ Lattice B ] in
/-- A key is in the map iff it is in the (fixed) key list
(Agda: `∈k - exclusive` becomes a special case). -/
theorem memKey_iff { k : A } { fm : FiniteMap A B ks } : MemKey k fm ↔ k ∈ ks : = by
rw [ MemKey , fm . property ]
/-- Agda: `∈k - dec`. -/
instance { k : A } { fm : FiniteMap A B ks } [ DecidableEq A ] :
Decidable ( MemKey k fm ) : =
decidable_of_iff _ memKey_iff . symm
omit [ Lattice B ] in
/-- Agda: `forget`. -/
theorem mem_key_of_mem { k : A } { v : B } { fm : FiniteMap A B ks }
( h : ( k , v ) ∈ fm ) : MemKey k fm : =
List . mem_map_of_mem _ h
section Locate
variable [ DecidableEq A ]
private def locateList ( k : A ) :
( l : List ( A × B ) ) → k ∈ l . map Prod . fst → { v : B / / ( k , v ) ∈ l }
| [ ] , h = > absurd h ( by simp )
| p :: l' , h = >
if heq : p . 1 = k then
⟨ p . 2 , by rw [ ← heq ] ; exact List . mem_cons_self . . ⟩
else
let ⟨ v , hv ⟩ : = locateList k l' ( by
rcases List . mem_cons . mp h with h' | h'
· exact absurd h' . symm heq
· exact h' )
⟨ v , List . mem_cons_of_mem _ hv ⟩
/-- Agda: `locate`. -/
def locate { k : A } { fm : FiniteMap A B ks } ( h : MemKey k fm ) :
{ v : B / / ( k , v ) ∈ fm } : =
locateList k fm . val h
end Locate
/- ! ### The pointwise order -/
theorem combine_eq_right_iff : ∀ { l₁ l₂ : List ( A × B ) } ,
l₁ . map Prod . fst = l₂ . map Prod . fst →
( combine ( · ⊔ · ) l₁ l₂ = l₂ ↔
List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 ) l₁ l₂ )
| [ ] , [ ] , _ = > by simp [ combine ]
| p :: l₁ , q :: l₂ , h = > by
simp only [ List . map_cons , List . cons . injEq ] at h
simp only [ combine , List . zipWith_cons_cons , List . cons . injEq ,
List . forall ₂ _ cons , Prod . ext_iff ]
rw [ show List . zipWith ( fun p q : A × B = > ( p . 1 , p . 2 ⊔ q . 2 ) ) l₁ l₂
= combine ( · ⊔ · ) l₁ l₂ from rfl ,
combine_eq_right_iff h . 2 ]
constructor
· rintro ⟨ ⟨ hk , hv ⟩ , hrest ⟩
exact ⟨ ⟨ hk , sup_eq_right . mp hv ⟩ , hrest ⟩
· rintro ⟨ ⟨ hk , hv ⟩ , hrest ⟩
exact ⟨ ⟨ hk , sup_eq_right . mpr hv ⟩ , hrest ⟩
| [ ] , _ :: _ , h = > by simp at h
| _ :: _ , [ ] , h = > by simp at h
/-- The order on finite maps is the pointwise order on values. -/
theorem le_iff { fm₁ fm₂ : FiniteMap A B ks } :
fm₁ ≤ fm₂ ↔
List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 ) fm₁ . val fm₂ . val : = by
rw [ ← sup_eq_right , ← combine_eq_right_iff ( spine_eq fm₁ fm₂ ) , Subtype . ext_iff ,
sup_val ]
private theorem forall ₂ _ spine : ∀ { l₁ l₂ : List ( A × B ) } ,
List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 ) l₁ l₂ →
l₁ . map Prod . fst = l₂ . map Prod . fst
| _ , _ , List . Forall₂ . nil = > rfl
| _ , _ , List . Forall₂ . cons hpq hrest = > by
simp [ List . map_cons , hpq . 1 , forall ₂ _ spine hrest ]
private theorem forall ₂ _ mem_mem { l₁ l₂ : List ( A × B ) }
( hf : List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 ) l₁ l₂ ) :
( l₁ . map Prod . fst ) . Nodup →
∀ { k : A } { v₁ v₂ : B } , ( k , v₁ ) ∈ l₁ → ( k , v₂ ) ∈ l₂ → v₁ ≤ v₂ : = by
induction hf with
| nil = >
intro _ k v₁ v₂ h₁ _
simp at h₁
| @ cons p q l₁' l₂' hpq hrest ih = >
intro hnd k v₁ v₂ h₁ h₂
simp only [ List . map_cons , List . nodup_cons ] at hnd
have hspine : = forall ₂ _ spine hrest
rcases List . mem_cons . mp h₁ with heq₁ | h₁'
· rcases List . mem_cons . mp h₂ with heq₂ | h₂'
· rw [ ← heq₁ , ← heq₂ ] at hpq
exact hpq . 2
· exfalso
apply hnd . 1
rw [ show p . 1 = k from ( congrArg Prod . fst heq₁ ) . symm , hspine ]
exact List . mem_map_of_mem _ h₂'
· rcases List . mem_cons . mp h₂ with heq₂ | h₂'
· exfalso
apply hnd . 1
rw [ hpq . 1 , show q . 1 = k from ( congrArg Prod . fst heq₂ ) . symm ]
exact List . mem_map_of_mem _ h₁'
· exact ih hnd . 2 h₁' h₂'
/-- Agda: `m₁≼m₂⇒m₁[k]≼m₂[k]`. The `Nodup` hypothesis was carried inside the
Agda `Map` type. -/
theorem le_of_mem_mem ( hks : ks . Nodup ) { fm₁ fm₂ : FiniteMap A B ks }
( hle : fm₁ ≤ fm₂ ) { k : A } { v₁ v₂ : B }
( h₁ : ( k , v₁ ) ∈ fm₁ ) ( h₂ : ( k , v₂ ) ∈ fm₂ ) : v₁ ≤ v₂ : =
forall ₂ _ mem_mem ( le_iff . mp hle ) ( fm₁ . property . symm ▸ hks ) h₁ h₂
/- ! ### Provenance of joined values -/
omit [ Lattice B ] in
private theorem mem_combine ( f : B → B → B ) : ∀ { l₁ l₂ : List ( A × B ) } { k : A } { v : B } ,
l₁ . map Prod . fst = l₂ . map Prod . fst →
( k , v ) ∈ combine f l₁ l₂ →
∃ v₁ v₂ , v = f v₁ v₂ ∧ ( k , v₁ ) ∈ l₁ ∧ ( k , v₂ ) ∈ l₂
| [ ] , [ ] , _ , _ , _ , h = > by simp [ combine ] at h
| p :: l₁ , q :: l₂ , k , v , hsp , h = > by
simp only [ List . map_cons , List . cons . injEq ] at hsp
simp only [ combine , List . zipWith_cons_cons ] at h
rcases List . mem_cons . mp h with heq | h'
· injection heq with hk hv
exact ⟨ p . 2 , q . 2 , hv ,
by rw [ hk ] ; simp ,
by rw [ hk , hsp . 1 ] ; simp ⟩
· obtain ⟨ v₁ , v₂ , hv , h₁ , h₂ ⟩ : = mem_combine f hsp . 2 h'
exact ⟨ v₁ , v₂ , hv , List . mem_cons_of_mem _ h₁ , List . mem_cons_of_mem _ h₂ ⟩
/-- Agda: `Provenance - union` — a binding of a join comes from bindings of both
maps. -/
theorem mem_sup { fm₁ fm₂ : FiniteMap A B ks } { k : A } { v : B }
( h : ( k , v ) ∈ fm₁ ⊔ fm₂ ) :
∃ v₁ v₂ , v = v₁ ⊔ v₂ ∧ ( k , v₁ ) ∈ fm₁ ∧ ( k , v₂ ) ∈ fm₂ : =
mem_combine _ ( spine_eq fm₁ fm₂ ) h
/- ! ### Updating (Agda: `_updating_via_` and `GeneralizedUpdate`) -/
section Updating
variable [ DecidableEq A ]
/-- Agda: `_updating_via_` — for each key in `ks'`, replace its value by `g k`. -/
def updating ( fm : FiniteMap A B ks ) ( ks' : List A ) ( g : A → B ) :
FiniteMap A B ks : =
⟨ fm . val . map ( fun p = > if p . 1 ∈ ks' then ( p . 1 , g p . 1 ) else p ) , by
rw [ List . map_map ,
show ( Prod . fst ∘ fun p : A × B = > if p . 1 ∈ ks' then ( p . 1 , g p . 1 ) else p )
= Prod . fst from funext fun p = > by by_cases h : p . 1 ∈ ks' < ; > simp [ h ] ]
exact fm . property ⟩
omit [ Lattice B ] in
@[ simp ] theorem updating_val ( fm : FiniteMap A B ks ) ( ks' : List A ) ( g : A → B ) :
( updating fm ks' g ) . val
= fm . val . map ( fun p = > if p . 1 ∈ ks' then ( p . 1 , g p . 1 ) else p ) : = rfl
omit [ Lattice B ] in
/-- Agda: `updating - via - ∈k - forward` (strengthened to an iff). -/
theorem memKey_updating { k : A } { fm : FiniteMap A B ks } { ks' : List A } { g : A → B } :
MemKey k ( updating fm ks' g ) ↔ MemKey k fm : = by
rw [ memKey_iff , memKey_iff ]
omit [ Lattice B ] in
/-- Agda: `updating - via - k∈ks - ≡`. -/
theorem eq_of_mem_updating { k : A } { v : B } { fm : FiniteMap A B ks }
{ ks' : List A } { g : A → B } ( hk : k ∈ ks' )
( h : ( k , v ) ∈ updating fm ks' g ) : v = g k : = by
obtain ⟨ p , hp , heq ⟩ : = List . mem_map . mp h
by_cases hmem : p . 1 ∈ ks'
· rw [ if_pos hmem ] at heq
injection heq with h1 h2
rw [ ← h2 , h1 ]
· rw [ if_neg hmem ] at heq
rw [ heq ] at hmem
exact absurd hk hmem
omit [ Lattice B ] in
/-- Agda: `updating - via - k∈ks`. -/
theorem mem_updating { k : A } { fm : FiniteMap A B ks } { ks' : List A } { g : A → B }
( hk : k ∈ ks' ) ( hmem : MemKey k fm ) : ( k , g k ) ∈ updating fm ks' g : = by
obtain ⟨ v , hv ⟩ : = locate hmem
exact List . mem_map . mpr ⟨ ( k , v ) , hv , by simp [ hk ] ⟩
omit [ Lattice B ] in
/-- Agda: `updating - via - k∉ks - forward`. -/
theorem mem_updating_of_not_mem { k : A } { v : B } { fm : FiniteMap A B ks }
{ ks' : List A } { g : A → B } ( hk : k ∉ ks' ) ( h : ( k , v ) ∈ fm ) :
( k , v ) ∈ updating fm ks' g : =
List . mem_map . mpr ⟨ ( k , v ) , h , by simp [ hk ] ⟩
omit [ Lattice B ] in
/-- Agda: `updating - via - k∉ks - backward`. -/
theorem mem_of_mem_updating { k : A } { v : B } { fm : FiniteMap A B ks }
{ ks' : List A } { g : A → B } ( hk : k ∉ ks' )
( h : ( k , v ) ∈ updating fm ks' g ) : ( k , v ) ∈ fm : = by
obtain ⟨ p , hp , heq ⟩ : = List . mem_map . mp h
by_cases hmem : p . 1 ∈ ks'
· rw [ if_pos hmem ] at heq
injection heq with h1 _
rw [ ← h1 ] at hk
exact absurd hmem hk
· rw [ if_neg hmem ] at heq
exact heq ▸ hp
private theorem updating_mono_list { ks' : List A } { g₁ g₂ : A → B }
( hg : ∀ k , g₁ k ≤ g₂ k ) { l₁ l₂ : List ( A × B ) }
( hl : List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 ) l₁ l₂ ) :
List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 )
( l₁ . map fun p = > if p . 1 ∈ ks' then ( p . 1 , g₁ p . 1 ) else p )
( l₂ . map fun p = > if p . 1 ∈ ks' then ( p . 1 , g₂ p . 1 ) else p ) : = by
induction hl with
| nil = > exact List . Forall₂ . nil
| @ cons x y l₁' l₂' hpq hrest ih = >
simp only [ List . map_cons ]
refine List . Forall₂ . cons ? _ ih
obtain ⟨ hk , hv ⟩ : = hpq
by_cases h : x . 1 ∈ ks'
· rw [ if_pos h , if_pos ( hk ▸ h ) ]
exact ⟨ hk , hk ▸ hg x . 1 ⟩
· rw [ if_neg h , if_neg ( fun hy = > h ( hk . symm ▸ hy ) ) ]
exact ⟨ hk , hv ⟩
/-- Agda: `f' - Monotonic` at the `Map` level. -/
theorem updating_mono { fm₁ fm₂ : FiniteMap A B ks } { ks' : List A }
{ g₁ g₂ : A → B } ( hfm : fm₁ ≤ fm₂ ) ( hg : ∀ k , g₁ k ≤ g₂ k ) :
updating fm₁ ks' g₁ ≤ updating fm₂ ks' g₂ : = by
rw [ le_iff ] at hfm ⊢
simp only [ updating_val ]
exact updating_mono_list hg hfm
end Updating
section GeneralizedUpdate
/- ! Agda: `GeneralizedUpdate` (the "Exercise 4.26" construction). -/
variable [ DecidableEq A ] { L : Type * } [ Lattice L ]
/-- Agda: `GeneralizedUpdate.f'`. -/
def generalizedUpdate ( f : L → FiniteMap A B ks ) ( g : A → L → B )
( ks' : List A ) ( l : L ) : FiniteMap A B ks : =
( f l ) . updating ks' ( fun k = > g k l )
variable { f : L → FiniteMap A B ks } { g : A → L → B } { ks' : List A }
/-- Agda: `f' - Monotonic`. -/
theorem generalizedUpdate_monotone ( hf : Monotone f )
( hg : ∀ k , Monotone ( g k ) ) : Monotone ( generalizedUpdate f g ks' ) : =
fun _ _ hl = > updating_mono ( hf hl ) ( fun k = > hg k hl )
omit [ Lattice B ] [ Lattice L ] in
/-- Agda: `f' - ∈k - forward`. -/
theorem generalizedUpdate_memKey { k : A } { l : L }
( h : MemKey k ( f l ) ) : MemKey k ( generalizedUpdate f g ks' l ) : = by
unfold generalizedUpdate
exact memKey_updating . mpr h
omit [ Lattice B ] [ Lattice L ] in
/-- Agda: `f' - k∈ks`. -/
theorem generalizedUpdate_mem { k : A } { l : L } ( hk : k ∈ ks' )
( h : MemKey k ( f l ) ) : ( k , g k l ) ∈ generalizedUpdate f g ks' l : = by
unfold generalizedUpdate
exact mem_updating hk h
omit [ Lattice B ] [ Lattice L ] in
/-- Agda: `f' - k∈ks - ≡`. -/
theorem generalizedUpdate_mem_eq { k : A } { v : B } { l : L } ( hk : k ∈ ks' )
( h : ( k , v ) ∈ generalizedUpdate f g ks' l ) : v = g k l : = by
unfold generalizedUpdate at h
exact eq_of_mem_updating ( g : = fun k = > g k l ) hk h
omit [ Lattice B ] [ Lattice L ] in
/-- Agda: `f' - k∉ks - forward`. -/
theorem generalizedUpdate_not_mem_forward { k : A } { v : B } { l : L } ( hk : k ∉ ks' )
( h : ( k , v ) ∈ f l ) : ( k , v ) ∈ generalizedUpdate f g ks' l : = by
unfold generalizedUpdate
exact mem_updating_of_not_mem hk h
omit [ Lattice B ] [ Lattice L ] in
/-- Agda: `f' - k∉ks - backward`. -/
theorem generalizedUpdate_not_mem_backward { k : A } { v : B } { l : L } ( hk : k ∉ ks' )
( h : ( k , v ) ∈ generalizedUpdate f g ks' l ) : ( k , v ) ∈ f l : = by
unfold generalizedUpdate at h
exact mem_of_mem_updating hk h
end GeneralizedUpdate
/- ! ### Reading off values at a list of keys (Agda: `_[_]`) -/
section ValuesAt
variable [ DecidableEq A ]
private def lookup? ( k : A ) : List ( A × B ) → Option B
| [ ] = > none
| p :: l' = > if p . 1 = k then some p . 2 else lookup? k l'
/-- Agda: `_[_]`. -/
def valuesAt ( fm : FiniteMap A B ks ) ( ks' : List A ) : List B : =
ks' . filterMap ( fun k = > lookup? k fm . val )
omit [ Lattice B ] in
private theorem lookup?_eq_some_of_mem : ∀ { l : List ( A × B ) } ,
( l . map Prod . fst ) . Nodup → ∀ { k : A } { v : B } , ( k , v ) ∈ l →
lookup? k l = some v
| [ ] , _ , _ , _ , h = > by simp at h
| p :: l' , hnd , k , v , h = > by
simp only [ List . map_cons , List . nodup_cons ] at hnd
rcases List . mem_cons . mp h with heq | h'
· rw [ ← heq ]
simp [ lookup? ]
· rw [ lookup? , if_neg ? _ ]
· exact lookup?_eq_some_of_mem hnd . 2 h'
· intro hpk
subst hpk
have : = List . mem_map_of_mem Prod . fst h'
exact hnd . 1 this
omit [ Lattice B ] in
/-- Agda: `[] - ∈`. -/
theorem mem_valuesAt ( hks : ks . Nodup ) { fm : FiniteMap A B ks } { k : A } { v : B }
{ ks' : List A } ( hk : k ∈ ks' ) ( h : ( k , v ) ∈ fm ) : v ∈ valuesAt fm ks' : =
List . mem_filterMap . mpr
⟨ k , hk , lookup?_eq_some_of_mem ( fm . property . symm ▸ hks ) h ⟩
private theorem lookup?_forall₂ { l₁ l₂ : List ( A × B ) }
( h : List . Forall₂ ( fun p q : A × B = > p . 1 = q . 1 ∧ p . 2 ≤ q . 2 ) l₁ l₂ ) ( k : A ) :
Option . Rel ( · ≤ · ) ( lookup? k l₁ ) ( lookup? k l₂ ) : = by
induction h with
| nil = > exact Option . Rel . none
| @ cons p q l₁ l₂ hpq hrest ih = >
rw [ lookup? , lookup? ]
by_cases hc : q . 1 = k
· rw [ if_pos hc , if_pos ( hpq . 1 . trans hc ) ]
exact Option . Rel . some hpq . 2
· rw [ if_neg hc , if_neg ( fun hp = > hc ( hpq . 1 ▸ hp ) ) ]
exact ih
/-- Agda: `m₁≼m₂⇒m₁[ks]≼m₂[ks]`. -/
theorem valuesAt_le { fm₁ fm₂ : FiniteMap A B ks } ( hle : fm₁ ≤ fm₂ )
( ks' : List A ) :
List . Forall₂ ( · ≤ · ) ( valuesAt fm₁ ks' ) ( valuesAt fm₂ ks' ) : = by
induction ks' with
| nil = > exact List . Forall₂ . nil
| cons k ks'' ih = >
have hrel : = lookup?_forall₂ ( le_iff . mp hle ) k
rw [ valuesAt , valuesAt , List . filterMap_cons , List . filterMap_cons ]
revert hrel
generalize lookup? k fm₁ . val = o₁
generalize lookup? k fm₂ . val = o₂
intro hrel
cases hrel with
| none = > simpa [ valuesAt ] using ih
| some hv = > exact List . Forall₂ . cons hv ( by simpa [ valuesAt ] using ih )
end ValuesAt
/- ! ### The isomorphism with `IterProd` and the fixed height -/
section Iso
omit [ Lattice B ] in
theorem val_ne_nil { k : A } { ks' : List A } ( fm : FiniteMap A B ( k :: ks' ) ) :
fm . val ≠ [ ] : = fun h = > by
have hp : = fm . property
rw [ h ] at hp
simp at hp
def headVal { k : A } { ks' : List A } : FiniteMap A B ( k :: ks' ) → B
| ⟨ [ ] , h ⟩ = > absurd h ( by simp )
| ⟨ p :: _ , _ ⟩ = > p . 2
/-- Agda: `pop`. -/
def pop { k : A } { ks' : List A } : FiniteMap A B ( k :: ks' ) → FiniteMap A B ks'
| ⟨ [ ] , h ⟩ = > absurd h ( by simp )
| ⟨ _ :: l , h ⟩ = >
⟨ l , by simp only [ List . map_cons , List . cons . injEq ] at h ; exact h . 2 ⟩
omit [ Lattice B ] in
theorem val_eq_cons { k : A } { ks' : List A } :
∀ fm : FiniteMap A B ( k :: ks' ) , fm . val = ( k , fm . headVal ) :: fm . pop . val
| ⟨ [ ] , h ⟩ = > absurd h ( by simp )
| ⟨ p :: l , h ⟩ = > by
simp only [ List . map_cons , List . cons . injEq ] at h
simp [ headVal , pop , ← h . 1 ]
/-- Agda: `IterProdIsomorphism.from`. -/
def toIter : { ks : List A } → FiniteMap A B ks → IterProd B PUnit ks . length
| [ ] , _ = > PUnit . unit
| _ :: _ , fm = > ( fm . headVal , toIter fm . pop )
/-- Agda: `IterProdIsomorphism.to` (no `Unique ks` needed: the spine - pinned
representation is canonical). -/
def ofIter : ( ks : List A ) → IterProd B PUnit ks . length → FiniteMap A B ks
| [ ] , _ = > ⟨ [ ] , rfl ⟩
| k :: ks' , ip = >
⟨ ( k , ip . 1 ) :: ( ofIter ks' ip . 2 ) . val , by
simp [ ( ofIter ks' ip . 2 ) . property ] ⟩
omit [ Lattice B ] in
/-- Agda: `from - to - inverseʳ`. -/
theorem ofIter_toIter : ∀ { ks : List A } ( fm : FiniteMap A B ks ) ,
ofIter ks ( toIter fm ) = fm
| [ ] , fm = > by
obtain ⟨ val , hprop ⟩ : = fm
cases val with
| nil = > rfl
| cons p l = > exact absurd hprop ( by simp )
| k :: ks' , fm = > Subtype . ext ( by
show ( k , fm . headVal ) :: ( ofIter ks' ( toIter fm . pop ) ) . val = fm . val
rw [ ofIter_toIter fm . pop , ← val_eq_cons fm ] )
omit [ Lattice B ] in
/-- Agda: `from - to - inverseˡ`. -/
theorem toIter_ofIter : ∀ ( ks : List A ) ( ip : IterProd B PUnit ks . length ) ,
toIter ( ofIter ks ip ) = ip
| [ ] , _ = > rfl
| k :: ks' , ip = > by
show ( headVal ( ofIter ( k :: ks' ) ip ) , toIter ( pop ( ofIter ( k :: ks' ) ip ) ) ) = ip
rw [ show pop ( ofIter ( k :: ks' ) ip ) = ofIter ks' ip . 2 from rfl ,
toIter_ofIter ks' ip . 2 ]
rfl
theorem headVal_le { k : A } { ks' : List A } { fm₁ fm₂ : FiniteMap A B ( k :: ks' ) }
( h : fm₁ ≤ fm₂ ) : fm₁ . headVal ≤ fm₂ . headVal : = by
have h' : = le_iff . mp h
rw [ val_eq_cons fm₁ , val_eq_cons fm₂ ] at h'
exact ( List . forall ₂ _ cons . mp h' ) . 1 . 2
theorem pop_le { k : A } { ks' : List A } { fm₁ fm₂ : FiniteMap A B ( k :: ks' ) }
( h : fm₁ ≤ fm₂ ) : fm₁ . pop ≤ fm₂ . pop : = by
rw [ le_iff ]
have h' : = le_iff . mp h
rw [ val_eq_cons fm₁ , val_eq_cons fm₂ ] at h'
exact ( List . forall ₂ _ cons . mp h' ) . 2
/-- Agda: `from - preserves - ≈` and `from - ⊔ - distr` (see header note). -/
theorem toIter_monotone : ∀ { ks : List A } ,
Monotone ( toIter : FiniteMap A B ks → IterProd B PUnit ks . length )
| [ ] = > fun _ _ _ = > le_refl _
| _ :: _ = > fun _ _ h = >
Prod . mk_le_mk . mpr ⟨ headVal_le h , toIter_monotone ( pop_le h ) ⟩
/-- Agda: `to - preserves - ≈` and `to - ⊔ - distr` (see header note). -/
theorem ofIter_monotone : ∀ ( ks : List A ) , Monotone ( ofIter ( A : = A ) ( B : = B ) ks )
| [ ] = > fun _ _ _ = > le_refl _
| k :: ks' = > fun ip₁ ip₂ h = > by
rw [ le_iff ]
show List . Forall₂ _ ( ( k , ip₁ . 1 ) :: ( ofIter ks' ip₁ . 2 ) . val )
( ( k , ip₂ . 1 ) :: ( ofIter ks' ip₂ . 2 ) . val )
exact List . Forall₂ . cons ⟨ rfl , h . 1 ⟩ ( le_iff . mp ( ofIter_monotone ks' h . 2 ) )
/-- Agda: `FixedHeight.fixedHeight` — a finite map into a lattice of height
`hB` has height `|ks| · hB`, by transport along the `IterProd` isomorphism. -/
def fixedHeight { hB : ℕ } ( fhB : FixedHeight B hB ) ( ks : List A ) :
FixedHeight ( FiniteMap A B ks ) ( ks . length * hB ) : =
( ( IterProd . fixedHeight fhB punitFixedHeight ks . length ) . transport
( ofIter ks ) toIter ( ofIter_monotone ks ) toIter_monotone
( toIter_ofIter ks ) ( fun fm = > ofIter_toIter fm ) ) . cast ( by ring )
omit [ Lattice B ] in
/-- Agda: `to - build`. -/
theorem mem_ofIter_build { b : B } : ∀ { ks : List A } { k : A } { v : B } ,
( k , v ) ∈ ofIter ks ( IterProd . build b PUnit . unit ks . length ) → v = b
| [ ] , _ , _ , h = > by simp [ ofIter , mem_def ] at h
| k' :: ks' , k , v , h = > by
rcases List . mem_cons . mp h with heq | h'
· exact ( Prod . ext_iff . mp heq ) . 2
· exact mem_ofIter_build h'
/-- Agda: `⊥ - contains - bottoms`. -/
theorem bot_contains_bots { hB : ℕ } ( fhB : FixedHeight B hB ) { k : A } { v : B }
( h : ( k , v ) ∈ ( fixedHeight fhB ks ) . bot ) : v = fhB . bot : = by
have hbot : ( fixedHeight fhB ks ) . bot
= ofIter ks ( IterProd . build fhB . bot PUnit . unit ks . length ) : = by
show ofIter ks ( IterProd . fixedHeight fhB punitFixedHeight ks . length ) . bot = _
rw [ IterProd . bot_fixedHeight ]
rw [ hbot ] at h
exact mem_ofIter_build h
end Iso
end FiniteMap
end Spa