This PR reworks the `simp` set around the `Id` monad, to not elide or unfold `pure` and `Id.run` In particular, it stops encoding the "defeq abuse" of `Id X = X` in the statements of theorems, instead using `Id.run` and `pure` to pass back and forth between these two spellings. Often when writing these with `pure`, they generalize to other lawful monads; though such changes were split off to other PRs. This fixes the problem with the current simp set where `Id.run (pure x)` is simplified to `Id.run x`, instead of the desirable `x`. This is particularly bad because the` x` is sometimes inferred with type `Id X` instead of `X`, which prevents other `simp` lemmas about `X` from firing. Making `Id` reducible instead is not an option, as then the `Monad` instances would have nothing to key on. --------- Co-authored-by: Sebastian Graf <sg@lean-fro.org> Co-authored-by: Kim Morrison <kim@tqft.net> Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
675 lines
30 KiB
Text
675 lines
30 KiB
Text
/-
|
||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Markus Himmel
|
||
-/
|
||
prelude
|
||
import Init.Data.Array.TakeDrop
|
||
import Std.Data.DHashMap.Basic
|
||
import Std.Data.DHashMap.Internal.HashesTo
|
||
import Std.Data.DHashMap.Internal.AssocList.Lemmas
|
||
|
||
/-!
|
||
This is an internal implementation file of the hash map. Users of the hash map should not rely on
|
||
the contents of this file.
|
||
|
||
In this file we define functions for manipulating a hash map based on operations defined in terms of
|
||
their buckets. Then we give "model implementations" of the hash map operations in terms of these
|
||
basic building blocks and show that the actual operations are equal to the model implementations T
|
||
his means that later we will be able to prove properties of the operations by proving general facts
|
||
about the basic building blocks.
|
||
-/
|
||
|
||
set_option linter.missingDocs true
|
||
set_option autoImplicit false
|
||
|
||
universe u v w
|
||
|
||
variable {α : Type u} {β : α → Type v} {γ : Type w} {δ : α → Type w}
|
||
|
||
open List (Perm perm_append_comm_assoc)
|
||
|
||
namespace Std.DHashMap.Internal
|
||
|
||
open Std.DHashMap.Internal.List
|
||
open Std.Internal.List
|
||
open Std.Internal
|
||
|
||
/-! # Setting up the infrastructure -/
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def bucket [Hashable α] (self : Array (AssocList α β)) (h : 0 < self.size) (k : α) :
|
||
AssocList α β :=
|
||
let ⟨i, h⟩ := mkIdx self.size h (hash k)
|
||
self[i]
|
||
|
||
theorem bucket_eq {α : Type u} {β : α → Type v} [Hashable α] (self : Array (AssocList α β))
|
||
(h : 0 < self.size) (k : α) : bucket self h k =
|
||
haveI := mkIdx self.size h (hash k) |>.2
|
||
self[mkIdx self.size h (hash k) |>.1] := rfl
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def updateBucket [Hashable α] (self : Array (AssocList α β)) (h : 0 < self.size) (k : α)
|
||
(f : AssocList α β → AssocList α β) : Array (AssocList α β) :=
|
||
let ⟨i, h⟩ := mkIdx self.size h (hash k)
|
||
self.uset i (f self[i]) h
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def updateAllBuckets (self : Array (AssocList α β)) (f : AssocList α β → AssocList α δ) :
|
||
Array (AssocList α δ) :=
|
||
self.map f
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def withComputedSize (self : Array (AssocList α β)) : Raw α β :=
|
||
⟨computeSize self, self⟩
|
||
|
||
@[simp]
|
||
theorem size_updateBucket [Hashable α] {self : Array (AssocList α β)} {h : 0 < self.size} {k : α}
|
||
{f : AssocList α β → AssocList α β} : (updateBucket self h k f).size = self.size := by
|
||
simp [updateBucket]
|
||
|
||
@[simp]
|
||
theorem size_updateAllBuckets {self : Array (AssocList α β)} {f : AssocList α β → AssocList α δ} :
|
||
(updateAllBuckets self f).size = self.size := by
|
||
simp [updateAllBuckets]
|
||
|
||
@[simp]
|
||
theorem buckets_size_withComputedSize {self : Array (AssocList α β)} :
|
||
(withComputedSize self).2.size = self.size := by
|
||
simp [withComputedSize]
|
||
|
||
@[simp]
|
||
theorem size_withComputedSize {self : Array (AssocList α β)} :
|
||
(withComputedSize self).size = computeSize self := rfl
|
||
|
||
@[simp]
|
||
theorem buckets_withComputedSize {self : Array (AssocList α β)} :
|
||
(withComputedSize self).buckets = self := rfl
|
||
|
||
@[simp]
|
||
theorem bucket_updateBucket [Hashable α] (self : Array (AssocList α β)) (h : 0 < self.size) (k : α)
|
||
(f : AssocList α β → AssocList α β) :
|
||
bucket (updateBucket self h k f) (by simpa using h) k = f (bucket self h k) := by
|
||
unfold bucket updateBucket mkIdx
|
||
simp
|
||
|
||
theorem exists_bucket_of_uset [BEq α] [Hashable α]
|
||
(self : Array (AssocList α β)) (i : USize) (hi : i.toNat < self.size) (d : AssocList α β) :
|
||
∃ l, Perm (toListModel self) (self[i.toNat].toList ++ l) ∧
|
||
Perm (toListModel (self.uset i d hi)) (d.toList ++ l) ∧
|
||
(∀ [LawfulHashable α], IsHashSelf self →
|
||
∀ k : α, (mkIdx self.size (by omega) (hash k)).1.toNat = i.toNat →
|
||
containsKey k l = false) := by
|
||
have h₀ : 0 < self.size := by omega
|
||
obtain ⟨l₁, l₂, h₁, h₂, h₃⟩ := Array.exists_of_uset hi
|
||
refine ⟨l₁.flatMap AssocList.toList ++ l₂.flatMap AssocList.toList, ?_, ?_, ?_⟩
|
||
· rw [toListModel, h₁]
|
||
simpa using perm_append_comm_assoc _ _ _
|
||
· rw [toListModel, h₃]
|
||
simpa using perm_append_comm_assoc _ _ _
|
||
· intro _ h k hki
|
||
simp only [containsKey_append, Bool.or_eq_false_iff]
|
||
refine ⟨?_, ?_⟩
|
||
· apply List.containsKey_flatMap_eq_false
|
||
intro j hj
|
||
rw [List.getElem_append_left' (l₂ := self[i] :: l₂), getElem_congr_coll h₁.symm]
|
||
apply (h.hashes_to j _).containsKey_eq_false h₀ k
|
||
omega
|
||
· apply List.containsKey_flatMap_eq_false
|
||
intro j hj
|
||
rw [← List.getElem_cons_succ self[i] _ _
|
||
(by simp only [Array.ugetElem_eq_getElem, List.length_cons]; omega)]
|
||
rw [List.getElem_append_right' l₁, getElem_congr_coll h₁.symm]
|
||
apply (h.hashes_to (j + 1 + l₁.length) _).containsKey_eq_false h₀ k
|
||
omega
|
||
|
||
theorem exists_bucket_of_update [BEq α] [Hashable α] (m : Array (AssocList α β)) (h : 0 < m.size)
|
||
(k : α) (f : AssocList α β → AssocList α β) :
|
||
∃ l : List ((a : α) × β a),
|
||
Perm (toListModel m) ((bucket m h k).toList ++ l) ∧
|
||
Perm (toListModel (updateBucket m h k f)) ((f (bucket m h k)).toList ++ l) ∧
|
||
(∀ [LawfulHashable α], IsHashSelf m → ∀ k', hash k = hash k' → containsKey k' l = false) := by
|
||
let idx := mkIdx m.size h (hash k)
|
||
obtain ⟨l, h₁, h₂, h₃⟩ := exists_bucket_of_uset m idx.1 idx.2 (f m[idx.1])
|
||
exact ⟨l, h₁, h₂, fun h k' hk' => h₃ h _ (hk' ▸ rfl)⟩
|
||
|
||
theorem exists_bucket' [BEq α] [Hashable α]
|
||
(self : Array (AssocList α β)) (i : USize) (hi : i.toNat < self.size) :
|
||
∃ l, Perm (self.toList.flatMap AssocList.toList) (self[i.toNat].toList ++ l) ∧
|
||
(∀ [LawfulHashable α], IsHashSelf self → ∀ k,
|
||
(mkIdx self.size (by omega) (hash k)).1.toNat = i.toNat → containsKey k l = false) := by
|
||
obtain ⟨l, h₁, -, h₂⟩ := exists_bucket_of_uset self i hi .nil
|
||
exact ⟨l, h₁, h₂⟩
|
||
|
||
theorem exists_bucket [BEq α] [Hashable α]
|
||
(m : Array (AssocList α β)) (h : 0 < m.size) (k : α) :
|
||
∃ l : List ((a : α) × β a), Perm (toListModel m) ((bucket m h k).toList ++ l) ∧
|
||
(∀ [LawfulHashable α], IsHashSelf m → ∀ k', hash k = hash k' → containsKey k' l = false) := by
|
||
obtain ⟨l, h₁, -, h₂⟩ := exists_bucket_of_update m h k (fun _ => .nil)
|
||
exact ⟨l, h₁, h₂⟩
|
||
|
||
/-- This is the general theorem used to show that access operations are correct. -/
|
||
theorem apply_bucket [BEq α] [Hashable α] [PartialEquivBEq α] [LawfulHashable α] {m : Raw₀ α β}
|
||
(hm : Raw.WFImp m.1) {a : α} {f : AssocList α β → γ} {g : List ((a : α) × β a) → γ}
|
||
(hfg : ∀ {l}, f l = g l.toList) (hg₁ : ∀ {l l'}, DistinctKeys l → Perm l l' → g l = g l')
|
||
(hg₂ : ∀ {l l'}, containsKey a l' = false → g (l ++ l') = g l) :
|
||
f (bucket m.1.buckets m.2 a) = g (toListModel m.1.buckets) := by
|
||
obtain ⟨l, hl, hlk⟩ := exists_bucket m.1.buckets m.2 a
|
||
refine Eq.trans ?_ (hg₁ (hm.distinct.perm hl.symm) hl.symm)
|
||
rw [hfg, hg₂]
|
||
exact hlk hm.buckets_hash_self _ rfl
|
||
|
||
/-- This is the general theorem used to show that access operations involving a proof (like `get`)
|
||
are correct. -/
|
||
theorem apply_bucket_with_proof {γ : α → Type w} [BEq α] [Hashable α] [PartialEquivBEq α]
|
||
[LawfulHashable α] {m : Raw₀ α β} (hm : Raw.WFImp m.1) (a : α)
|
||
(f : (a : α) → (l : AssocList α β) → l.contains a → γ a)
|
||
(g : (a : α) → (l : List ((a : α) × β a)) → containsKey a l → γ a)
|
||
(hfg : ∀ {a l h}, f a l h = g a l.toList (AssocList.contains_eq.symm.trans h))
|
||
(hg₁ : ∀ {l l' a h}, DistinctKeys l → (hl' : Perm l l') →
|
||
g a l h = g a l' ((List.containsKey_of_perm hl').symm.trans h)) {h h'}
|
||
(hg₂ : ∀ {l l' a h}, (hl' : containsKey a l' = false) →
|
||
g a (l ++ l') h = g a l ((List.containsKey_append_of_not_contains_right hl').symm.trans h)) :
|
||
f a (bucket m.1.buckets m.2 a) h = g a (toListModel m.1.buckets) h' := by
|
||
obtain ⟨l, hl, hlk⟩ := exists_bucket m.1.buckets m.2 a
|
||
refine Eq.trans ?_ (hg₁ hm.distinct hl).symm
|
||
rw [hfg, hg₂]
|
||
exact hlk hm.buckets_hash_self _ rfl
|
||
|
||
/-- This is the general theorem to show that modification operations are correct. -/
|
||
theorem toListModel_updateBucket [BEq α] [Hashable α] [PartialEquivBEq α] [LawfulHashable α]
|
||
{m : Raw₀ α β} (hm : Raw.WFImp m.1) {a : α} {f : AssocList α β → AssocList α β}
|
||
{g : List ((a : α) × β a) → List ((a : α) × β a)} (hfg : ∀ {l}, Perm (f l).toList (g l.toList))
|
||
(hg₁ : ∀ {l l'}, DistinctKeys l → Perm l l' → Perm (g l) (g l'))
|
||
(hg₂ : ∀ {l l'}, containsKey a l' = false → g (l ++ l') = g l ++ l') :
|
||
Perm (toListModel (updateBucket m.1.buckets m.2 a f)) (g (toListModel m.1.2)) := by
|
||
obtain ⟨l, h₁, h₂, h₃⟩ := exists_bucket_of_update m.1.buckets m.2 a f
|
||
refine h₂.trans (Perm.trans ?_ (hg₁ hm.distinct h₁).symm)
|
||
refine Perm.append_right l hfg |>.trans ?_
|
||
rw [hg₂]
|
||
exact h₃ hm.buckets_hash_self _ rfl
|
||
|
||
/-- This is the general theorem to show that mapping operations (like `map` and `filter`) are
|
||
correct. -/
|
||
theorem toListModel_updateAllBuckets {m : Raw₀ α β} {f : AssocList α β → AssocList α δ}
|
||
{g : List ((a : α) × β a) → List ((a : α) × δ a)}
|
||
(hfg : ∀ {l}, Perm ((f l).toList) (g l.toList))
|
||
(hg : ∀ {l l'}, Perm (g (l ++ l')) (g l ++ g l')) :
|
||
Perm (toListModel (updateAllBuckets m.1.buckets f)) (g (toListModel m.1.2)) := by
|
||
have hg₀ : g [] = [] := by
|
||
rw [← List.length_eq_zero_iff]
|
||
have := (hg (l := []) (l' := [])).length_eq
|
||
rw [List.length_append, List.append_nil] at this
|
||
omega
|
||
rw [updateAllBuckets, toListModel, Array.toList_map, List.flatMap_eq_foldl, List.foldl_map,
|
||
toListModel, List.flatMap_eq_foldl]
|
||
suffices ∀ (l : List (AssocList α β)) (l' : List ((a : α) × δ a)) (l'' : List ((a : α) × β a)),
|
||
Perm (g l'') l' →
|
||
Perm (l.foldl (fun acc a => acc ++ (f a).toList) l')
|
||
(g (l.foldl (fun acc a => acc ++ a.toList) l'')) by
|
||
simpa using this m.1.buckets.toList [] [] (by simp [hg₀])
|
||
rintro l l' l'' h
|
||
induction l generalizing l' l''
|
||
· simpa using h.symm
|
||
· next l t ih =>
|
||
simp only [List.foldl_cons]
|
||
apply ih
|
||
exact hg.trans (Perm.append h hfg.symm)
|
||
|
||
/-! # IsHashSelf -/
|
||
|
||
namespace IsHashSelf
|
||
|
||
@[simp]
|
||
theorem replicate [BEq α] [Hashable α] {c : Nat} : IsHashSelf
|
||
(Array.replicate c (AssocList.nil : AssocList α β)) :=
|
||
⟨by simp⟩
|
||
|
||
set_option linter.missingDocs false in
|
||
@[deprecated replicate (since := "2025-03-18")]
|
||
abbrev mkArray := @replicate
|
||
|
||
theorem uset [BEq α] [Hashable α] {m : Array (AssocList α β)} {i : USize} {h : i.toNat < m.size}
|
||
{d : AssocList α β}
|
||
(hd : HashesTo m[i].toList i.toNat m.size → HashesTo d.toList i.toNat m.size)
|
||
(hm : IsHashSelf m) : IsHashSelf (m.uset i d h) := by
|
||
refine ⟨fun j hj => ?_⟩
|
||
simp only [Array.uset, Array.getElem_set, Array.size_set]
|
||
split
|
||
· next hij => exact hij ▸ (hd (hm.hashes_to _ _))
|
||
· exact hm.hashes_to j (by simpa using hj)
|
||
|
||
/-- This is the general theorem to show that modification operations preserve well-formedness of
|
||
buckets. -/
|
||
theorem updateBucket [BEq α] [Hashable α] [PartialEquivBEq α] [LawfulHashable α]
|
||
{m : Array (AssocList α β)} {h : 0 < m.size} {a : α} {f : AssocList α β → AssocList α β}
|
||
(hf : ∀ l p, p ∈ (f l).toList → containsKey p.1 l.toList ∨ hash p.1 = hash a)
|
||
(hm : IsHashSelf m) : IsHashSelf (updateBucket m h a f) := by
|
||
rw [Internal.updateBucket]
|
||
refine IsHashSelf.uset (fun h' => ⟨fun _ p hp => ?_⟩) hm
|
||
rcases hf _ _ hp with (hf|hf)
|
||
· rw [containsKey_eq_true_iff_exists_mem] at hf
|
||
rcases hf with ⟨q, hq₁, hq₂⟩
|
||
rw [← h'.hash_self h _ hq₁, hash_eq hq₂]
|
||
· rw [hf]
|
||
|
||
theorem updateAllBuckets [BEq α] [Hashable α] [LawfulHashable α] {m : Array (AssocList α β)}
|
||
{f : AssocList α β → AssocList α δ} (hf : ∀ l p, p ∈ (f l).toList → containsKey p.1 l.toList)
|
||
(hm : IsHashSelf m) : IsHashSelf (updateAllBuckets m f) := by
|
||
rw [Internal.updateAllBuckets]
|
||
refine ⟨fun j hj => ?_⟩
|
||
simp only [Array.getElem_map, Array.size_map]
|
||
refine ⟨fun h p hp => ?_⟩
|
||
rcases containsKey_eq_true_iff_exists_mem.1 (hf _ _ hp) with ⟨q, hq₁, hq₂⟩
|
||
rw [← hash_eq hq₂, (hm.hashes_to _ _).hash_self _ _ hq₁]
|
||
|
||
end IsHashSelf
|
||
|
||
namespace Raw₀
|
||
|
||
/-! # Definition of model functions -/
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def replaceₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) : Raw₀ α β :=
|
||
⟨⟨m.1.size, updateBucket m.1.buckets m.2 a (fun l => l.replace a b)⟩, by simpa using m.2⟩
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def consₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) : Raw₀ α β :=
|
||
⟨⟨m.1.size + 1, updateBucket m.1.buckets m.2 a (fun l => l.cons a b)⟩, by simpa using m.2⟩
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def get?ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Option (β a) :=
|
||
(bucket m.1.buckets m.2 a).getCast? a
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def getKey?ₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Option α :=
|
||
(bucket m.1.buckets m.2 a).getKey? a
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def containsₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Bool :=
|
||
(bucket m.1.buckets m.2 a).contains a
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def getₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) (h : m.containsₘ a) : β a :=
|
||
(bucket m.1.buckets m.2 a).getCast a h
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def getDₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) (fallback : β a) : β a :=
|
||
(m.get?ₘ a).getD fallback
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def get!ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) [Inhabited (β a)] : β a :=
|
||
(m.get?ₘ a).get!
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def getKeyₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (h : m.containsₘ a) : α :=
|
||
(bucket m.1.buckets m.2 a).getKey a h
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def getKeyDₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (fallback : α) : α :=
|
||
(m.getKey?ₘ a).getD fallback
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def getKey!ₘ [BEq α] [Hashable α] [Inhabited α] (m : Raw₀ α β) (a : α) : α :=
|
||
(m.getKey?ₘ a).get!
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def insertₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) : Raw₀ α β :=
|
||
if m.containsₘ a then m.replaceₘ a b else Raw₀.expandIfNecessary (m.consₘ a b)
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def insertIfNewₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) : Raw₀ α β :=
|
||
if m.containsₘ a then m else Raw₀.expandIfNecessary (m.consₘ a b)
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def eraseₘaux [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||
⟨⟨m.1.size - 1, updateBucket m.1.buckets m.2 a (fun l => l.erase a)⟩, by simpa using m.2⟩
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def eraseₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) : Raw₀ α β :=
|
||
if m.containsₘ a then m.eraseₘaux a else m
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def alterₘ [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α)
|
||
(f : Option (β a) → Option (β a)) : Raw₀ α β :=
|
||
if h : m.containsₘ a then
|
||
let buckets' := updateBucket m.1.buckets m.2 a (fun l => l.alter a f)
|
||
let size' :=
|
||
if Raw₀.containsₘ ⟨withComputedSize buckets', by simpa [buckets'] using m.2⟩ a
|
||
then m.1.size else m.1.size - 1
|
||
⟨⟨size', buckets'⟩, by simpa [buckets'] using m.2⟩
|
||
else
|
||
match f none with
|
||
| none => m
|
||
| some b => Raw₀.expandIfNecessary (m.consₘ a b)
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def modifyₘ [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α) (f : β a → β a) : Raw₀ α β :=
|
||
m.alterₘ a (·.map f)
|
||
|
||
namespace Const
|
||
|
||
variable {β : Type v}
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def alterₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α)
|
||
(f : Option β → Option β) : Raw₀ α (fun _ => β) :=
|
||
if h : m.containsₘ a then
|
||
let buckets' := updateBucket m.1.buckets m.2 a (fun l => AssocList.Const.alter a f l)
|
||
let size' :=
|
||
if Raw₀.containsₘ ⟨withComputedSize buckets', by simpa [buckets'] using m.2⟩ a
|
||
then m.1.size else m.1.size - 1
|
||
⟨⟨size', buckets'⟩, by simpa [buckets'] using m.2⟩
|
||
else
|
||
match f none with
|
||
| none => m
|
||
| some b => Raw₀.expandIfNecessary (m.consₘ a b)
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def modifyₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) (f : β → β) :
|
||
Raw₀ α (fun _ => β) :=
|
||
alterₘ m a (fun option => option.map f)
|
||
|
||
end Const
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def filterMapₘ (m : Raw₀ α β) (f : (a : α) → β a → Option (δ a)) : Raw₀ α δ :=
|
||
⟨withComputedSize (updateAllBuckets m.1.buckets fun l => l.filterMap f), by simpa using m.2⟩
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def mapₘ (m : Raw₀ α β) (f : (a : α) → β a → δ a) : Raw₀ α δ :=
|
||
⟨⟨m.1.size, updateAllBuckets m.1.buckets (AssocList.map f)⟩, by simpa using m.2⟩
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def filterₘ (m : Raw₀ α β) (f : (a : α) → β a → Bool) : Raw₀ α β :=
|
||
⟨withComputedSize (updateAllBuckets m.1.buckets fun l => l.filter f), by simpa using m.2⟩
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def insertListₘ [BEq α] [Hashable α] (m : Raw₀ α β) (l : List ((a : α) × β a)) : Raw₀ α β :=
|
||
match l with
|
||
| .nil => m
|
||
| .cons hd tl => insertListₘ (m.insert hd.1 hd.2) tl
|
||
|
||
section
|
||
|
||
variable {β : Type v}
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def Const.get?ₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) : Option β :=
|
||
(bucket m.1.buckets m.2 a).get? a
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def Const.getₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) (h : m.containsₘ a) : β :=
|
||
(bucket m.1.buckets m.2 a).get a h
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def Const.getDₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) (fallback : β) : β :=
|
||
(Const.get?ₘ m a).getD fallback
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def Const.get!ₘ [BEq α] [Hashable α] [Inhabited β] (m : Raw₀ α (fun _ => β)) (a : α) : β :=
|
||
(Const.get?ₘ m a).get!
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def Const.insertListₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (l: List (α × β)) :
|
||
Raw₀ α (fun _ => β) :=
|
||
match l with
|
||
| .nil => m
|
||
| .cons hd tl => insertListₘ (m.insert hd.1 hd.2) tl
|
||
|
||
/-- Internal implementation detail of the hash map -/
|
||
def Const.insertListIfNewUnitₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => Unit)) (l: List α) :
|
||
Raw₀ α (fun _ => Unit) :=
|
||
match l with
|
||
| .nil => m
|
||
| .cons hd tl => insertListIfNewUnitₘ (m.insertIfNew hd ()) tl
|
||
|
||
end
|
||
|
||
/-! # Equivalence between model functions and real implementations -/
|
||
|
||
theorem reinsertAux_eq [Hashable α] (data : { d : Array (AssocList α β) // 0 < d.size }) (a : α)
|
||
(b : β a) :
|
||
(reinsertAux hash data a b).1 = updateBucket data.1 data.2 a (fun l => l.cons a b) := rfl
|
||
|
||
theorem get?_eq_get?ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
|
||
get? m a = get?ₘ m a := rfl
|
||
|
||
theorem get_eq_getₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) (h : m.contains a) :
|
||
get m a h = getₘ m a h := rfl
|
||
|
||
theorem getD_eq_getDₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) (fallback : β a) :
|
||
getD m a fallback = getDₘ m a fallback := by
|
||
simp [getD, getDₘ, get?ₘ, List.getValueCastD_eq_getValueCast?, bucket]
|
||
|
||
theorem get!_eq_get!ₘ [BEq α] [LawfulBEq α] [Hashable α] (m : Raw₀ α β) (a : α) [Inhabited (β a)] :
|
||
get! m a = get!ₘ m a := by
|
||
simp [get!, get!ₘ, get?ₘ, List.getValueCast!_eq_getValueCast?, bucket]
|
||
|
||
theorem getKey?_eq_getKey?ₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
|
||
getKey? m a = getKey?ₘ m a := rfl
|
||
|
||
theorem getKey_eq_getKeyₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (h : m.contains a) :
|
||
getKey m a h = getKeyₘ m a h := rfl
|
||
|
||
theorem getKeyD_eq_getKeyDₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a fallback : α) :
|
||
getKeyD m a fallback = getKeyDₘ m a fallback := by
|
||
simp [getKeyD, getKeyDₘ, getKey?ₘ, List.getKeyD_eq_getKey?, bucket]
|
||
|
||
theorem getKey!_eq_getKey!ₘ [BEq α] [Hashable α] [Inhabited α] (m : Raw₀ α β) (a : α) :
|
||
getKey! m a = getKey!ₘ m a := by
|
||
simp [getKey!, getKey!ₘ, getKey?ₘ, List.getKey!_eq_getKey?, bucket]
|
||
|
||
theorem contains_eq_containsₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
|
||
m.contains a = m.containsₘ a := rfl
|
||
|
||
theorem insert_eq_insertₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) :
|
||
m.insert a b = m.insertₘ a b := by
|
||
rw [insert, insertₘ, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split
|
||
· simp only [replaceₘ, Subtype.mk.injEq, Raw.mk.injEq, true_and]
|
||
rw [Array.set_set, updateBucket]
|
||
simp only [Array.uset, Array.ugetElem_eq_getElem]
|
||
· rfl
|
||
|
||
theorem alter_eq_alterₘ [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α)
|
||
(f : Option (β a) → Option (β a)) : m.alter a f = m.alterₘ a f := by
|
||
dsimp only [alter, alterₘ, containsₘ, ← bucket_eq]
|
||
split
|
||
· congr 2
|
||
· simp only [withComputedSize, bucket_updateBucket]
|
||
· simp only [Array.uset, bucket, Array.ugetElem_eq_getElem, Array.set_set, updateBucket]
|
||
· congr
|
||
|
||
theorem modify_eq_alter [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α)
|
||
(f : β a → β a) : m.modify a f = m.alter a (·.map f) := by
|
||
rw [modify, alter]
|
||
split
|
||
· dsimp
|
||
split
|
||
· next h =>
|
||
simp only [AssocList.contains_eq] at h
|
||
simp only [AssocList.modify_eq_alter, Array.set_set, AssocList.contains_eq,
|
||
containsKey_of_perm AssocList.toList_alter, ← modifyKey_eq_alterKey,
|
||
containsKey_modifyKey, h, reduceIte]
|
||
· rfl
|
||
|
||
theorem modify_eq_modifyₘ [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α)
|
||
(f : β a → β a) : m.modify a f = m.modifyₘ a f := by
|
||
rw [modify_eq_alter, alter_eq_alterₘ, modifyₘ]
|
||
|
||
namespace Const
|
||
|
||
variable {β : Type v}
|
||
|
||
theorem alter_eq_alterₘ [BEq α] [Hashable α] [EquivBEq α] (m : Raw₀ α (fun _ => β)) (a : α)
|
||
(f : Option β → Option β) : Const.alter m a f = Const.alterₘ m a f := by
|
||
dsimp only [alter, alterₘ, containsₘ, ← bucket_eq]
|
||
split
|
||
· congr 2
|
||
· simp only [withComputedSize, bucket_updateBucket]
|
||
· simp only [Array.uset, bucket, Array.ugetElem_eq_getElem, Array.set_set, updateBucket]
|
||
· congr
|
||
|
||
theorem modify_eq_alter [BEq α] [Hashable α] [EquivBEq α] (m : Raw₀ α (fun _ => β)) (a : α)
|
||
(f : β → β) : Const.modify m a f = Const.alter m a (·.map f) := by
|
||
rw [modify, alter]
|
||
split
|
||
· dsimp
|
||
split
|
||
· next h =>
|
||
simp only [AssocList.contains_eq] at h
|
||
simp only [AssocList.Const.modify_eq_alter, Array.set_set, AssocList.contains_eq,
|
||
containsKey_of_perm AssocList.Const.toList_alter, ← Const.modifyKey_eq_alterKey,
|
||
Const.containsKey_modifyKey, h, reduceIte]
|
||
· rfl
|
||
|
||
theorem modify_eq_modifyₘ [BEq α] [Hashable α] [EquivBEq α] (m : Raw₀ α (fun _ => β)) (a : α)
|
||
(f : β → β) : Const.modify m a f = Const.modifyₘ m a f := by
|
||
rw [modify_eq_alter, alter_eq_alterₘ, modifyₘ]
|
||
|
||
end Const
|
||
|
||
theorem containsThenInsert_eq_insertₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) :
|
||
(m.containsThenInsert a b).2 = m.insertₘ a b := by
|
||
rw [containsThenInsert, insertₘ, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split
|
||
· simp only [replaceₘ, Subtype.mk.injEq, Raw.mk.injEq, true_and]
|
||
rw [Array.set_set, updateBucket]
|
||
simp only [Array.uset, Array.ugetElem_eq_getElem]
|
||
· rfl
|
||
|
||
theorem containsThenInsert_eq_containsₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) :
|
||
(m.containsThenInsert a b).1 = m.containsₘ a := by
|
||
rw [containsThenInsert, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split <;> simp_all
|
||
|
||
theorem containsThenInsertIfNew_eq_insertIfNewₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α)
|
||
(b : β a) : (m.containsThenInsertIfNew a b).2 = m.insertIfNewₘ a b := by
|
||
rw [containsThenInsertIfNew, insertIfNewₘ, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split
|
||
· simp only [replaceₘ, Subtype.mk.injEq, Raw.mk.injEq, true_and]
|
||
· rfl
|
||
|
||
theorem containsThenInsertIfNew_eq_containsₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) :
|
||
(m.containsThenInsertIfNew a b).1 = m.containsₘ a := by
|
||
rw [containsThenInsertIfNew, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split <;> simp_all
|
||
|
||
theorem insertIfNew_eq_insertIfNewₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) (b : β a) :
|
||
m.insertIfNew a b = m.insertIfNewₘ a b := rfl
|
||
|
||
theorem getThenInsertIfNew?_eq_insertIfNewₘ [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β)
|
||
(a : α) (b : β a) : (m.getThenInsertIfNew? a b).2 = m.insertIfNewₘ a b := by
|
||
rw [getThenInsertIfNew?, insertIfNewₘ, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split <;> simp_all [consₘ, updateBucket, List.containsKey_eq_isSome_getValueCast?]
|
||
|
||
theorem getThenInsertIfNew?_eq_get?ₘ [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α)
|
||
(b : β a) : (m.getThenInsertIfNew? a b).1 = m.get?ₘ a := by
|
||
rw [getThenInsertIfNew?, get?ₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split <;> simp_all
|
||
|
||
theorem erase_eq_eraseₘ [BEq α] [Hashable α] (m : Raw₀ α β) (a : α) :
|
||
m.erase a = m.eraseₘ a := by
|
||
rw [erase, eraseₘ, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split
|
||
· simp only [eraseₘaux, Subtype.mk.injEq, Raw.mk.injEq, true_and]
|
||
rw [Array.set_set, updateBucket]
|
||
simp only [Array.uset, Array.ugetElem_eq_getElem]
|
||
· rfl
|
||
|
||
theorem filterMap_eq_filterMapₘ (m : Raw₀ α β) (f : (a : α) → β a → Option (δ a)) :
|
||
m.filterMap f = m.filterMapₘ f := rfl
|
||
|
||
theorem map_eq_mapₘ (m : Raw₀ α β) (f : (a : α) → β a → δ a) :
|
||
m.map f = m.mapₘ f := rfl
|
||
|
||
theorem filter_eq_filterₘ (m : Raw₀ α β) (f : (a : α) → β a → Bool) :
|
||
m.filter f = m.filterₘ f := rfl
|
||
|
||
theorem insertMany_eq_insertListₘ [BEq α] [Hashable α] (m : Raw₀ α β) (l : List ((a : α) × β a)) : insertMany m l = insertListₘ m l := by
|
||
simp only [insertMany, Id.run_pure, Id.run_bind, pure_bind, List.forIn_pure_yield_eq_foldl]
|
||
suffices ∀ (t : { m' // ∀ (P : Raw₀ α β → Prop),
|
||
(∀ {m'' : Raw₀ α β} {a : α} {b : β a}, P m'' → P (m''.insert a b)) → P m → P m' }),
|
||
(List.foldl (fun m' p => ⟨m'.val.insert p.1 p.2, fun P h₁ h₂ => h₁ (m'.2 _ h₁ h₂)⟩) t l).val =
|
||
t.val.insertListₘ l from this _
|
||
intro t
|
||
induction l generalizing m with
|
||
| nil => simp [insertListₘ]
|
||
| cons hd tl ih =>
|
||
simp only [List.foldl_cons, insertListₘ]
|
||
apply ih
|
||
|
||
section
|
||
|
||
variable {β : Type v}
|
||
|
||
theorem Const.get?_eq_get?ₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) :
|
||
Const.get? m a = Const.get?ₘ m a := rfl
|
||
|
||
theorem Const.get_eq_getₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α)
|
||
(h : m.contains a) : Const.get m a h = Const.getₘ m a h := rfl
|
||
|
||
theorem Const.getD_eq_getDₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) (fallback : β) :
|
||
Const.getD m a fallback = Const.getDₘ m a fallback := by
|
||
simp [getD, getDₘ, get?ₘ, List.getValueD_eq_getValue?, bucket]
|
||
|
||
theorem Const.get!_eq_get!ₘ [BEq α] [Hashable α] [Inhabited β] (m : Raw₀ α (fun _ => β)) (a : α) :
|
||
Const.get! m a = Const.get!ₘ m a := by
|
||
simp [get!, get!ₘ, get?ₘ, List.getValue!_eq_getValue?, bucket]
|
||
|
||
theorem Const.getThenInsertIfNew?_eq_insertIfNewₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β))
|
||
(a : α) (b : β) : (Const.getThenInsertIfNew? m a b).2 = m.insertIfNewₘ a b := by
|
||
rw [getThenInsertIfNew?, insertIfNewₘ, containsₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split <;> simp_all [consₘ, updateBucket, List.containsKey_eq_isSome_getValue?, -Option.isSome_eq_false_iff]
|
||
|
||
theorem Const.getThenInsertIfNew?_eq_get?ₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α)
|
||
(b : β) : (Const.getThenInsertIfNew? m a b).1 = Const.get?ₘ m a := by
|
||
rw [getThenInsertIfNew?, get?ₘ, bucket]
|
||
dsimp only [Array.ugetElem_eq_getElem, Array.uset]
|
||
split <;> simp_all [-getValue?_eq_none]
|
||
|
||
theorem Const.insertMany_eq_insertListₘ [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β))
|
||
(l : List (α × β)) :
|
||
(Const.insertMany m l).1 = Const.insertListₘ m l := by
|
||
simp only [insertMany, Id.run_pure, pure_bind, List.forIn_pure_yield_eq_foldl]
|
||
suffices ∀ (t : { m' // ∀ (P : Raw₀ α (fun _ => β) → Prop),
|
||
(∀ {m'' : Raw₀ α (fun _ => β)} {a : α} {b : β}, P m'' → P (m''.insert a b)) → P m → P m' }),
|
||
(List.foldl (fun m' p => ⟨m'.val.insert p.1 p.2, fun P h₁ h₂ => h₁ (m'.2 _ h₁ h₂)⟩) t l).val =
|
||
Const.insertListₘ t.val l from this _
|
||
intro t
|
||
induction l generalizing m with
|
||
| nil => simp [insertListₘ]
|
||
| cons hd tl ih =>
|
||
simp only [List.foldl_cons, insertListₘ]
|
||
apply ih
|
||
|
||
theorem Const.insertManyIfNewUnit_eq_insertListIfNewUnitₘ [BEq α] [Hashable α]
|
||
(m : Raw₀ α (fun _ => Unit)) (l: List α):
|
||
(Const.insertManyIfNewUnit m l).1 = Const.insertListIfNewUnitₘ m l := by
|
||
simp only [insertManyIfNewUnit, Id.run_pure, Id.run_bind, pure_bind, List.forIn_pure_yield_eq_foldl]
|
||
suffices ∀ (t : { m' // ∀ (P : Raw₀ α (fun _ => Unit) → Prop),
|
||
(∀ {m'' a b}, P m'' → P (m''.insertIfNew a b)) → P m → P m'}),
|
||
(List.foldl (fun m' p => ⟨m'.val.insertIfNew p (), fun P h₁ h₂ => h₁ (m'.2 _ h₁ h₂)⟩) t l).val =
|
||
Const.insertListIfNewUnitₘ t.val l from this _
|
||
intro t
|
||
induction l generalizing m with
|
||
| nil => simp [insertListIfNewUnitₘ]
|
||
| cons hd tl ih =>
|
||
simp only [List.foldl_cons, insertListIfNewUnitₘ]
|
||
apply ih
|
||
|
||
end
|
||
|
||
end Raw₀
|
||
|
||
end Std.DHashMap.Internal
|