feat: add BEq to DHashMap/HashMap/HashSet and their extensional variants (#11266)

This PR adds `BEq` instance for `DHashMap`/`HashMap`/`HashSet` and their
extensional variants and proves lemmas relating it to the equivalence of
hashmaps/equality of extensional variants.
This commit is contained in:
Wojciech Różowski 2025-12-10 16:40:09 +01:00 committed by GitHub
parent 72196169b6
commit ec008ff55a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 478 additions and 23 deletions

View file

@ -601,6 +601,12 @@ theorem sum_nat {l₁ l₂ : List Nat} (h : l₁ ~ l₂) : l₁.sum = l₂.sum :
| swap => simpa [List.sum_cons] using Nat.add_left_comm ..
| trans _ _ ih₁ ih₂ => simp [ih₁, ih₂]
theorem all_eq {l₁ l₂ : List α} {f : α → Bool} (hp : l₁.Perm l₂) : l₁.all f = l₂.all f := by
rw [Bool.eq_iff_iff]; simp [hp.mem_iff]
theorem any_eq {l₁ l₂ : List α} {f : α → Bool} (hp : l₁.Perm l₂) : l₁.any f = l₂.any f := by
rw [Bool.eq_iff_iff]; simp [hp.mem_iff]
grind_pattern Perm.sum_nat => l₁ ~ l₂, l₁.sum
grind_pattern Perm.sum_nat => l₁ ~ l₂, l₂.sum

View file

@ -360,6 +360,19 @@ instance [BEq α] [Hashable α] : Union (DHashMap α β) := ⟨union⟩
instance [BEq α] [Hashable α] : Inter (DHashMap α β) := ⟨inter⟩
instance [BEq α] [Hashable α] : SDiff (DHashMap α β) := ⟨diff⟩
/--
Compares two hash maps using Boolean equality on keys and values.
Returns `true` if the maps contain the same key-value pairs, `false` otherwise.
-/
def beq [LawfulBEq α] [∀ k, BEq (β k)] (a b : DHashMap α β) : Bool :=
Raw₀.beq ⟨a.1, a.2.size_buckets_pos⟩ ⟨b.1, b.2.size_buckets_pos⟩
instance [LawfulBEq α] [∀ k, BEq (β k)] : BEq (DHashMap α β) := ⟨beq⟩
@[inherit_doc DHashMap.beq] def Const.beq {β : Type v} [BEq α] [BEq β] (m₁ m₂ : DHashMap α (fun _ => β)) : Bool :=
Raw₀.Const.beq ⟨m₁.1, m₁.2.size_buckets_pos⟩ ⟨m₂.1, m₂.2.size_buckets_pos⟩
section Unverified
/-! We currently do not provide lemmas for the functions below. -/

View file

@ -490,6 +490,10 @@ def interSmaller [BEq α] [Hashable α] (m₁ : Raw₀ α β) (m₂ : Raw α β)
def inter [BEq α] [Hashable α] (m₁ m₂ : Raw₀ α β) : Raw₀ α β :=
if m₁.1.size ≤ m₂.1.size then m₁.filter fun k _ => m₂.contains k else interSmaller m₁ m₂
/-- Internal implementation detail of the hash map -/
def beq [BEq α] [LawfulBEq α] [Hashable α] [∀ k, BEq (β k)] (m₁ m₂ : Raw₀ α β) : Bool :=
if m₁.1.size ≠ m₂.1.size then false else m₁.1.all (fun k v => m₂.get? k == some v)
/-- Internal implementation detail of the hash map -/
@[inline] def diff [BEq α] [Hashable α] (m₁ m₂ : Raw₀ α β) : Raw₀ α β :=
if m₁.1.size ≤ m₂.1.size then m₁.filter (fun k _ => !m₂.contains k) else (eraseManyEntries m₁ m₂.1).1
@ -505,6 +509,10 @@ def Const.get? [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α) : O
let ⟨i, h⟩ := mkIdx buckets.size h (hash a)
buckets[i].get? a
/-- Internal implementation detail of the hash map -/
def Const.beq [BEq α] [Hashable α] [BEq β] (m₁ m₂ : Raw₀ α (fun _ => β)) : Bool :=
if m₁.1.size ≠ m₂.1.size then false else m₁.1.all (fun k v => Const.get? m₂ k == some v)
/-- Internal implementation detail of the hash map -/
def Const.get [BEq α] [Hashable α] (m : Raw₀ α (fun _ => β)) (a : α)
(hma : m.contains a) : β :=

View file

@ -154,6 +154,14 @@ theorem inter_eq [BEq α] [Hashable α] {m₁ m₂ : Raw α β} (h₁ : m₁.WF)
m₁.inter m₂ = Raw₀.inter ⟨m₁, h₁.size_buckets_pos⟩ ⟨m₂, h₂.size_buckets_pos⟩ := by
simp [Raw.inter, h₁.size_buckets_pos, h₂.size_buckets_pos]
theorem beq_eq [BEq α] [Hashable α] [LawfulBEq α] [∀ k, BEq (β k)] {m₁ m₂ : Raw α β} (h₁ : m₁.WF) (h₂ : m₂.WF) :
m₁.beq m₂ = Raw₀.beq ⟨m₁, h₁.size_buckets_pos⟩ ⟨m₂, h₂.size_buckets_pos⟩ := by
simp [Raw.beq, h₁.size_buckets_pos, h₂.size_buckets_pos]
theorem Const.beq_eq {β : Type v} [BEq α] [Hashable α] [BEq β] {m₁ m₂ : Raw α (fun _ => β)} (h₁ : m₁.WF) (h₂ : m₂.WF) :
Raw.Const.beq m₁ m₂ = Raw₀.Const.beq ⟨m₁, h₁.size_buckets_pos⟩ ⟨m₂, h₂.size_buckets_pos⟩ := by
simp [Raw.Const.beq, h₁.size_buckets_pos, h₂.size_buckets_pos]
theorem diff_eq [BEq α] [Hashable α] {m₁ m₂ : Raw α β} (h₁ : m₁.WF) (h₂ : m₂.WF) :
m₁.diff m₂ = Raw₀.diff ⟨m₁, h₁.size_buckets_pos⟩ ⟨m₂, h₂.size_buckets_pos⟩ := by
simp [Raw.diff, h₁.size_buckets_pos, h₂.size_buckets_pos]

View file

@ -11,6 +11,7 @@ import all Std.Data.DHashMap.Internal.Defs
public import Std.Data.DHashMap.Internal.WF
import all Std.Data.DHashMap.Raw
import all Std.Data.DHashMap.Basic
import all Std.Data.DHashMap.RawDef
meta import Std.Data.DHashMap.Basic
public section
@ -162,7 +163,10 @@ private meta def queryMap : Std.DHashMap Name (fun _ => Name × Array (MacroM (T
⟨`getEntry?, (``getEntry?_eq_getEntry?, #[`(getEntry?_of_perm _)])⟩,
⟨`getEntryD, (``getEntryD_eq_getEntryD, #[`(getEntryD_of_perm _)])⟩,
⟨`getEntry!, (``getEntry!_eq_getEntry!, #[`(getEntry!_of_perm _)])⟩,
⟨`all, (``Raw.all_eq_all_toListModel, #[])⟩,
⟨`toList, (``Raw.toList_eq_toListModel, #[])⟩,
⟨`Const.beq, (``Raw₀.Const.beq_eq_beqModel, #[])⟩,
⟨`beq, (``beq_eq_beqModel, #[])⟩,
⟨`keys, (``Raw.keys_eq_keys_toListModel, #[`(perm_keys_congr_left)])⟩,
⟨`Const.toList, (``Raw.Const.toList_eq_toListModel_map, #[`(perm_map_congr_left)])⟩,
⟨`foldM, (``Raw.foldM_eq_foldlM_toListModel, #[])⟩,
@ -1453,16 +1457,7 @@ theorem any_eq_false [LawfulBEq α] {p : (a : α) → β a → Bool} (h : m.1.WF
omit [Hashable α] [BEq α] in
theorem all_toList {p : (a : α) → β a → Bool} :
m.1.toList.all (fun x => p x.1 x.2) = m.1.all p := by
simp only [Raw.all, ForIn.forIn, Bool.not_eq_true, bind_pure_comp, map_pure, Id.run_bind]
rw [forIn_eq_forIn_toList, forIn_eq_forIn']
induction m.val.toList with
| nil => simp
| cons hd tl ih =>
simp only [forIn'_eq_forIn, List.all_cons]
by_cases h : p hd.fst hd.snd = false
· simp [h]
· simp only [forIn'_eq_forIn] at ih
simp [h, ih]
simp_to_model
omit [Hashable α] [BEq α] in
theorem all_eq_not_any_not {p : (a : α) → β a → Bool} :
@ -2590,8 +2585,38 @@ end Const
end insertMany
section Union
section BEq
variable {m₁ m₂ : Raw₀ α β} [LawfulBEq α] [∀ k, BEq (β k)]
theorem Equiv.beq [∀ k, ReflBEq (β k)] (h₁ : m₁.val.WF) (h₂ : m₂.val.WF) : m₁.1.Equiv m₂.1 → beq m₁ m₂ := by
simp_to_model using List.beqModel_eq_true_of_perm
theorem equiv_of_beq [∀ k, LawfulBEq (β k)] (h₁ : m₁.val.WF) (h₂ : m₂.val.WF) : beq m₁ m₂ = true → m₁.1.Equiv m₂.1 := by
simp_to_model using List.perm_of_beqModel
theorem Equiv.beq_congr {m₃ m₄ : Raw₀ α β} (h₁ : m₁.val.WF) (h₂ : m₂.val.WF) (h₃ : m₃.val.WF) (h₄ : m₄.val.WF) :
m₁.1.Equiv m₃.1 → m₂.1.Equiv m₄.1 → Raw₀.beq m₁ m₂ = Raw₀.beq m₃ m₄ := by
simp_to_model using List.beqModel_congr
end BEq
section
variable {β : Type v} {m₁ m₂ : Raw₀ α (fun _ => β)}
theorem Const.Equiv.beq [LawfulHashable α] [EquivBEq α] [BEq β] [ReflBEq β] (h₁ : m₁.val.WF) (h₂ : m₂.val.WF) : m₁.1.Equiv m₂.1 → Const.beq m₁ m₂ := by
simp_to_model using List.Const.beqModel_eq_true_of_perm
theorem Const.equiv_of_beq [LawfulBEq α] [BEq β] [LawfulBEq β] (h₁ : m₁.val.WF) (h₂ : m₂.val.WF) : beq m₁ m₂ = true → m₁.1.Equiv m₂.1 := by
simp_to_model using List.Const.perm_of_beqModel
theorem Const.Equiv.beq_congr [EquivBEq α] [LawfulHashable α] {m₃ m₄ : Raw₀ α (fun _ => β)} [BEq β] (h₁ : m₁.val.WF) (h₂ : m₂.val.WF) (h₃ : m₃.val.WF) (h₄ : m₄.val.WF) :
m₁.1.Equiv m₃.1 → m₂.1.Equiv m₄.1 → Const.beq m₁ m₂ = Const.beq m₃ m₄ := by
simp_to_model using List.Const.beqModel_congr
end
section Union
variable (m₁ m₂ : Raw₀ α β)
variable {m₁ m₂}

View file

@ -275,6 +275,19 @@ theorem forIn_eq_forIn_toListModel {δ : Type w} {l : Raw α β} {m : Type w →
· simp
· simpa using ih'
theorem all_eq_all_toListModel {p : (a: α) → β a → Bool} {m : Raw α β} :
m.all p = (toListModel m.buckets).all (fun x => p x.1 x.2) := by
simp only [Raw.all, ForIn.forIn, Bool.not_eq_true, bind_pure_comp, map_pure, Id.run_bind]
rw [forIn_eq_forIn_toListModel, ← toList_eq_toListModel, forIn_eq_forIn']
induction m.toList with
| nil => simp only [all_nil, forIn'_nil, Id.run_pure]
| cons hd tl ih =>
simp only [forIn'_eq_forIn, List.all_cons]
by_cases h : p hd.fst hd.snd = false
· simp [h]
· simp only [forIn'_eq_forIn] at ih
simp [h, ih]
end Raw
namespace Raw₀
@ -1584,4 +1597,13 @@ theorem Const.wf_insertManyIfNewUnit₀ [BEq α] [Hashable α] [EquivBEq α] [La
{l : ρ} (h' : m.WF) : (Const.insertManyIfNewUnit ⟨m, h⟩ l).1.1.WF :=
(Raw₀.Const.insertManyIfNewUnit ⟨m, h⟩ l).2 _ Raw.WF.insertIfNew₀ h'
theorem beq_eq_beqModel [BEq α] [LawfulBEq α] [Hashable α] [∀ k, BEq (β k)] {m₁ m₂ : Raw₀ α β} (h₁ : Raw.WFImp m₁.1) (h₂ : Raw.WFImp m₂.1) :
beq m₁ m₂ = beqModel (toListModel m₁.1.buckets) (toListModel m₂.1.buckets) := by
simp [beq, beqModel, Raw.size_eq_length h₁, Raw.size_eq_length h₂, Raw.all_eq_all_toListModel,
get?_eq_getValueCast? h₂]
theorem Const.beq_eq_beqModel {β : Type v} [BEq α] [PartialEquivBEq α] [Hashable α] [LawfulHashable α] [BEq β] {m₁ m₂ : Raw₀ α (fun _ => β)} (h₁ : Raw.WFImp m₁.1) (h₂ : Raw.WFImp m₂.1) :
beq m₁ m₂ = Const.beqModel (toListModel m₁.1.buckets) (toListModel m₂.1.buckets) := by
simp [beq, Const.beqModel, Raw.size_eq_length h₁, Raw.size_eq_length h₂, Raw.all_eq_all_toListModel, get?_eq_getValue? h₂]
end Raw₀

View file

@ -1840,6 +1840,34 @@ theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α]
{l : ρ} : (m.insertMany l).isEmpty → m.isEmpty :=
Raw₀.isEmpty_of_isEmpty_insertMany ⟨m.1, _⟩ m.2
section BEq
variable {m₁ m₂ : DHashMap α β} [LawfulBEq α] [∀ k, BEq (β k)]
theorem Equiv.beq [∀ k, ReflBEq (β k)] (h : m₁ ~m m₂) : m₁ == m₂ :=
Raw₀.Equiv.beq m₁.2 m₂.2 h.1
theorem equiv_of_beq [∀ k, LawfulBEq (β k)] (h : m₁ == m₂) : m₁ ~m m₂ :=
⟨Raw₀.equiv_of_beq m₁.2 m₂.2 h⟩
theorem Equiv.beq_congr {m₃ m₄ : DHashMap α β} (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : (m₁ == m₂) = (m₃ == m₄) :=
Raw₀.Equiv.beq_congr m₁.2 m₂.2 m₃.2 m₄.2 w₁.1 w₂.1
end BEq
section
variable {β : Type v} {m₁ m₂ : DHashMap α (fun _ => β)} [BEq β]
theorem Const.Equiv.beq [EquivBEq α] [LawfulHashable α] [ReflBEq β] (h : m₁ ~m m₂) : DHashMap.Const.beq m₁ m₂ :=
Raw₀.Const.Equiv.beq m₁.2 m₂.2 h.1
theorem Const.equiv_of_beq [LawfulBEq α] [LawfulBEq β] (h : Const.beq m₁ m₂) : m₁ ~m m₂ :=
⟨Raw₀.Const.equiv_of_beq m₁.2 m₂.2 h⟩
theorem Const.Equiv.beq_congr [EquivBEq α] [LawfulHashable α] {m₃ m₄ : DHashMap α (fun _ => β)} (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : Const.beq m₁ m₂ = Const.beq m₃ m₄ :=
Raw₀.Const.Equiv.beq_congr m₁.2 m₂.2 m₃.2 m₄.2 w₁.1 w₂.1
end
section Union
variable (m₁ m₂ : DHashMap α β)

View file

@ -476,17 +476,6 @@ only those mappings where the function returns `some` value.
@[inline] def keysArray (m : Raw α β) : Array α :=
m.fold (fun acc k _ => acc.push k) (.emptyWithCapacity m.size)
/-- Checks if all elements satisfy the predicate, short-circuiting if a predicate fails. -/
@[inline] def all (m : Raw α β) (p : (a : α) → β a → Bool) : Bool := Id.run do
for a in m do
if ¬ p a.1 a.2 then return false
return true
/-- Checks if any element satisfies the predicate, short-circuiting if a predicate succeeds. -/
@[inline] def any (m : Raw α β) (p : (a : α) → β a → Bool) : Bool := Id.run do
for a in m do
if p a.1 a.2 then return true
return false
/--
Computes the union of the given hash maps. If a key appears in both maps, the entry contained in
the second argument will appear in the result.
@ -522,6 +511,25 @@ This function always merges the smaller map into the larger map, so the expected
instance [BEq α] [Hashable α] : Inter (Raw α β) := ⟨inter⟩
/--
Compares two hash maps using Boolean equality on keys and values.
Returns `true` if the maps contain the same key-value pairs, `false` otherwise.
-/
def beq [BEq α] [Hashable α] [LawfulBEq α] [∀ k, BEq (β k)] (m₁ m₂ : Raw α β) : Bool :=
if h : 0 < m₁.buckets.size ∧ 0 < m₂.buckets.size then
Raw₀.beq ⟨m₁, h.1⟩ ⟨m₂, h.2⟩
else
false
instance [BEq α] [Hashable α] [LawfulBEq α] [∀ k, BEq (β k)] : BEq (Raw α β) := ⟨beq⟩
@[inherit_doc DHashMap.Raw.beq] def Const.beq {β : Type v} [BEq α] [Hashable α] [BEq β] (m₁ m₂ : Raw α (fun _ => β)) : Bool :=
if h : 0 < m₁.buckets.size ∧ 0 < m₂.buckets.size then
Raw₀.Const.beq ⟨m₁, h.1⟩ ⟨m₂, h.2⟩
else
false
/--
Computes the difference of the given hash maps.

View file

@ -81,6 +81,18 @@ instance [Monad m] : ForM m (Raw α β) ((a : α) × β a) where
instance [Monad m] : ForIn m (Raw α β) ((a : α) × β a) where
forIn m init f := m.forIn (fun a b acc => f ⟨a, b⟩ acc) init
/-- Checks if all elements satisfy the predicate, short-circuiting if a predicate fails. -/
@[inline] def all (m : Raw α β) (p : (a : α) → β a → Bool) : Bool := Id.run do
for a in m do
if ¬ p a.1 a.2 then return false
return true
/-- Checks if any element satisfies the predicate, short-circuiting if a predicate succeeds. -/
@[inline] def any (m : Raw α β) (p : (a : α) → β a → Bool) : Bool := Id.run do
for a in m do
if p a.1 a.2 then return true
return false
end Raw
end Std.DHashMap

View file

@ -48,7 +48,7 @@ private meta def baseNames : Array Name :=
``getKey?_eq, ``getKey_eq, ``getKey!_eq, ``getKeyD_eq,
``insertMany_eq, ``Const.insertMany_eq, ``Const.insertManyIfNewUnit_eq, ``ofArray_eq, ``Const.ofArray_eq,
``ofList_eq, ``Const.ofList_eq, ``Const.unitOfList_eq, ``Const.unitOfArray_eq,
``alter_eq, ``Const.alter_eq, ``modify_eq, ``Const.modify_eq, ``union_eq, ``inter_eq, ``diff_eq]
``alter_eq, ``Const.alter_eq, ``modify_eq, ``Const.modify_eq, ``union_eq, ``inter_eq, ``diff_eq, ``beq_eq, ``Const.beq_eq]
/-- Internal implementation detail of the hash map -/
@ -3761,6 +3761,40 @@ variable [BEq α] [Hashable α]
open Internal.Raw Internal.Raw₀
section BEq
variable {m₁ m₂ : Raw α β} [LawfulBEq α] [∀ k, BEq (β k)]
theorem Equiv.beq [∀ k, ReflBEq (β k)] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ ~m m₂) : m₁ == m₂ := by
simp only [BEq.beq]
simp_to_raw using Raw₀.Equiv.beq
theorem equiv_of_beq [∀ k, LawfulBEq (β k)] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ == m₂) : m₁ ~m m₂ := by
revert h
simp only [BEq.beq]
simp_to_raw using Raw₀.equiv_of_beq
theorem Equiv.beq_congr {m₃ m₄ : Raw α β} (h₁ : m₁.WF) (h₂ : m₂.WF) (h₃ : m₃.WF) (h₄ : m₄.WF) (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : (m₁ == m₂) = (m₃ == m₄) := by
simp only [BEq.beq]
simp_to_raw using Raw₀.Equiv.beq_congr
end BEq
section
variable {β : Type v} {m₁ m₂ : Raw α (fun _ => β)}
theorem Const.Equiv.beq [EquivBEq α] [LawfulHashable α] [BEq β] [ReflBEq β] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ ~m m₂) : beq m₁ m₂ := by
simp_to_raw using Raw₀.Const.Equiv.beq
theorem Const.equiv_of_beq [LawfulBEq α] [BEq β] [LawfulBEq β] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : beq m₁ m₂ = true) : m₁ ~m m₂ := by
revert h
simp_to_raw using Raw₀.Const.equiv_of_beq
theorem Const.Equiv.beq_congr [EquivBEq α] [LawfulHashable α] [BEq β] {m₃ m₄ : Raw α (fun _ => β)} (h₁ : m₁.WF) (h₂ : m₂.WF) (h₃ : m₃.WF) (h₄ : m₄.WF) (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : Raw.Const.beq m₁ m₂ = Raw.Const.beq m₃ m₄ := by
simp_to_raw using Raw₀.Const.Equiv.beq_congr
end
@[simp, grind =]
theorem ofArray_eq_ofList (a : Array ((a : α) × (β a))) :
ofArray a = ofList a.toList := by

View file

@ -362,6 +362,31 @@ def union [EquivBEq α] [LawfulHashable α] (m₁ m₂ : ExtDHashMap α β) : Ex
instance [EquivBEq α] [LawfulHashable α] : Union (ExtDHashMap α β) := ⟨union⟩
instance [LawfulBEq α] [∀ k, BEq (β k)] : BEq (ExtDHashMap α β) where
beq m₁ m₂ := lift₂ (fun x y : DHashMap α β => x.beq y) (fun _ _ _ _ => DHashMap.Equiv.beq_congr) m₁ m₂
instance [LawfulBEq α] [∀ k, BEq (β k)] [∀ k, ReflBEq (β k)] : ReflBEq (ExtDHashMap α β) where
rfl {a} := a.inductionOn fun x => DHashMap.Equiv.beq <| DHashMap.Equiv.refl x
instance [LawfulBEq α] [∀ k, BEq (β k)] [∀ k, LawfulBEq (β k)] : LawfulBEq (ExtDHashMap α β) where
eq_of_beq {m₁} {m₂} := m₁.inductionOn₂ m₂ fun _ _ hyp => sound <| DHashMap.equiv_of_beq hyp
namespace Const
variable {β : Type v}
@[inline, inherit_doc DHashMap.beq]
def beq [EquivBEq α] [LawfulHashable α] [BEq β] (m₁ m₂ : ExtDHashMap α fun _ => β) : Bool :=
lift₂ (fun x y : DHashMap α fun _ => β => DHashMap.Const.beq x y) (fun _ _ _ _ => DHashMap.Const.Equiv.beq_congr) m₁ m₂
theorem beq_of_eq [EquivBEq α] [LawfulHashable α] [BEq β] [ReflBEq β] (m₁ m₂ : ExtDHashMap α fun _ => β) : m₁ = m₂ → Const.beq m₁ m₂ :=
m₁.inductionOn₂ m₂ fun _ _ h => DHashMap.Const.Equiv.beq <| exact h
theorem eq_of_beq [LawfulBEq α] [BEq β] [LawfulBEq β] (m₁ m₂ : ExtDHashMap α fun _ => β) : Const.beq m₁ m₂ → m₁ = m₂ :=
m₁.inductionOn₂ m₂ fun _ _ h => sound <| DHashMap.Const.equiv_of_beq h
end Const
@[inline, inherit_doc DHashMap.inter]
def inter [EquivBEq α] [LawfulHashable α] (m₁ m₂ : ExtDHashMap α β) : ExtDHashMap α β := lift₂ (fun x y : DHashMap α β => mk (x.inter y))
(fun a b c d equiv₁ equiv₂ => by

View file

@ -249,6 +249,19 @@ def union [EquivBEq α] [LawfulHashable α] (m₁ m₂ : ExtHashMap α β) : Ext
instance [EquivBEq α] [LawfulHashable α] : Union (ExtHashMap α β) := ⟨union⟩
instance [EquivBEq α] [LawfulHashable α] [BEq β] : BEq (ExtHashMap α β) where
beq m₁ m₂ := ExtDHashMap.Const.beq m₁.inner m₂.inner
instance [EquivBEq α] [LawfulHashable α] [BEq β] [ReflBEq β] : ReflBEq (ExtHashMap α β) where
rfl := ExtDHashMap.Const.beq_of_eq _ _ rfl
instance [LawfulBEq α] [BEq β] [LawfulBEq β] : LawfulBEq (ExtHashMap α β) where
eq_of_beq {a} {b} hyp := by
have ⟨_⟩ := a
have ⟨_⟩ := b
simp only [mk.injEq] at |- hyp
exact ExtDHashMap.Const.eq_of_beq _ _ hyp
@[inline, inherit_doc ExtDHashMap.inter]
def inter [EquivBEq α] [LawfulHashable α] (m₁ m₂ : ExtHashMap α β) : ExtHashMap α β := ⟨ExtDHashMap.inter m₁.inner m₂.inner⟩

View file

@ -204,6 +204,19 @@ def union [EquivBEq α] [LawfulHashable α] (m₁ m₂ : ExtHashSet α) : ExtHas
instance [EquivBEq α] [LawfulHashable α] : Union (ExtHashSet α) := ⟨union⟩
instance [EquivBEq α] [LawfulHashable α] : BEq (ExtHashSet α) where
beq m₁ m₂ := ExtDHashMap.Const.beq m₁.inner.inner m₂.inner.inner
instance [EquivBEq α] [LawfulHashable α] : ReflBEq (ExtHashSet α) where
rfl := ExtDHashMap.Const.beq_of_eq _ _ rfl
instance [LawfulBEq α] : LawfulBEq (ExtHashSet α) where
eq_of_beq {a} {b} hyp := by
have ⟨⟨_⟩⟩ := a
have ⟨⟨_⟩⟩ := b
simp only [mk.injEq, ExtHashMap.mk.injEq] at |- hyp
exact ExtDHashMap.Const.eq_of_beq _ _ hyp
/--
Computes the intersection of the given hash sets.

View file

@ -278,6 +278,11 @@ instance [BEq α] [Hashable α] : Union (HashMap α β) := ⟨union⟩
instance [BEq α] [Hashable α] : Inter (HashMap α β) := ⟨inter⟩
@[inherit_doc DHashMap.beq] def beq {β : Type v} [BEq α] [BEq β] (m₁ m₂ : HashMap α β) : Bool :=
DHashMap.Const.beq m₁.inner m₂.inner
instance [BEq α] [BEq β] : BEq (HashMap α β) := ⟨beq⟩
@[inherit_doc DHashMap.inter, inline] def diff [BEq α] [Hashable α] (m₁ m₂ : HashMap α β) : HashMap α β :=
⟨DHashMap.diff m₁.inner m₂.inner⟩

View file

@ -1346,6 +1346,20 @@ theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α]
{l : ρ} : (insertMany m l).isEmpty → m.isEmpty :=
DHashMap.Const.isEmpty_of_isEmpty_insertMany
section
variable {β : Type v} {m₁ m₂ : HashMap α β} [BEq β]
theorem Equiv.beq [EquivBEq α] [LawfulHashable α] [ReflBEq β] (h : m₁ ~m m₂) : m₁ == m₂ :=
DHashMap.Const.Equiv.beq h.1
theorem equiv_of_beq [LawfulBEq α] [LawfulBEq β] (h : m₁ == m₂) : m₁ ~m m₂ :=
⟨DHashMap.Const.equiv_of_beq h⟩
theorem Equiv.beq_congr [EquivBEq α] [LawfulHashable α] {m₃ m₄ : HashMap α β} (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : (m₁ == m₂) = (m₃ == m₄) :=
DHashMap.Const.Equiv.beq_congr w₁.1 w₂.1
end
section Union
variable {β : Type v}

View file

@ -250,6 +250,11 @@ instance [BEq α] [Hashable α] : Inter (Raw α β) := ⟨inter⟩
instance [BEq α] [Hashable α] : SDiff (Raw α β) := ⟨diff⟩
@[inherit_doc DHashMap.Raw.beq] def beq {β : Type v} [BEq α] [Hashable α] [BEq β] (m₁ m₂ : Raw α β) : Bool :=
DHashMap.Raw.Const.beq m₁.inner m₂.inner
instance [BEq α] [Hashable α] [BEq β] : BEq (Raw α β) := ⟨beq⟩
section Unverified
@[inline, inherit_doc DHashMap.Raw.filterMap] def filterMap {γ : Type w} (f : α → β → Option γ)

View file

@ -1308,6 +1308,20 @@ theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.W
{l : ρ} : (insertMany m l).isEmpty → m.isEmpty :=
DHashMap.Raw.Const.isEmpty_of_isEmpty_insertMany h.out
section
variable {β : Type v} {m₁ m₂ : Raw α β}
theorem Equiv.beq [EquivBEq α] [LawfulHashable α] [BEq β] [ReflBEq β] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ ~m m₂) : m₁ == m₂ :=
DHashMap.Raw.Const.Equiv.beq h₁.1 h₂.1 h.1
theorem equiv_of_beq [LawfulBEq α] [BEq β] [LawfulBEq β] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ == m₂) : m₁ ~m m₂ :=
⟨DHashMap.Raw.Const.equiv_of_beq h₁.1 h₂.1 h⟩
theorem Equiv.beq_congr [EquivBEq α] [LawfulHashable α] [BEq β] {m₃ m₄ : Raw α β} (h₁ : m₁.WF) (h₂ : m₂.WF) (h₃ : m₃.WF) (h₄ : m₄.WF) (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : (m₁ == m₂) = (m₃ == m₄) :=
DHashMap.Raw.Const.Equiv.beq_congr h₁.1 h₂.1 h₃.1 h₄.1 w₁.1 w₂.1
end
section Union
variable {β : Type v}

View file

@ -259,6 +259,17 @@ This function always iterates through the smaller set, so the expected runtime i
instance [BEq α] [Hashable α] : Inter (HashSet α) := ⟨inter⟩
/--
Compares two hash sets using Boolean equality on keys.
Returns `true` if the sets contain the same keys, `false` otherwise.
-/
def beq [BEq α] (m₁ m₂ : HashSet α) : Bool :=
HashMap.beq m₁.inner m₂.inner
instance [BEq α] : BEq (HashSet α) := ⟨beq⟩
/--
Computes the difference of the given hash sets.

View file

@ -784,6 +784,20 @@ theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α]
end
section
variable {m₁ m₂ : HashSet α}
theorem Equiv.beq [EquivBEq α] [LawfulHashable α] (h : m₁ ~m m₂) : m₁ == m₂ :=
HashMap.Equiv.beq h.1
theorem equiv_of_beq [LawfulBEq α] (h : m₁ == m₂) : m₁ ~m m₂ :=
⟨HashMap.equiv_of_beq h⟩
theorem Equiv.beq_congr [EquivBEq α] [LawfulHashable α] {m₃ m₄ : HashSet α} (h₁ : m₁ ~m m₃) (h₂ : m₂ ~m m₄) : (m₁ == m₂) = (m₃ == m₄) :=
HashMap.Equiv.beq_congr h₁.1 h₂.1
end
section Union
variable (m₁ m₂ : HashSet α)

View file

@ -243,6 +243,17 @@ This function always merges the smaller set into the larger set, so the expected
instance [BEq α] [Hashable α] : Inter (Raw α) := ⟨inter⟩
/--
Compares two hash sets using Boolean equality on keys.
Returns `true` if the sets contain the same keys, `false` otherwise.
-/
def beq [BEq α] [Hashable α] (m₁ m₂ : Raw α) : Bool :=
HashMap.Raw.beq m₁.inner m₂.inner
instance [BEq α] [Hashable α] : BEq (Raw α) := ⟨beq⟩
/--
Computes the difference of the given hash sets.

View file

@ -816,6 +816,20 @@ theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.W
{l : ρ} : (insertMany m l).isEmpty → m.isEmpty :=
HashMap.Raw.isEmpty_of_isEmpty_insertManyIfNewUnit h.out
section
variable {m₁ m₂ : Raw α}
theorem Equiv.beq [EquivBEq α] [LawfulHashable α] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ ~m m₂) : beq m₁ m₂ :=
HashMap.Raw.Equiv.beq h₁.1 h₂.1 h.1
theorem equiv_of_beq [LawfulBEq α] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : m₁ == m₂) : m₁ ~m m₂ :=
⟨HashMap.Raw.equiv_of_beq h₁.1 h₂.1 h⟩
theorem Equiv.beq_congr [EquivBEq α] [LawfulHashable α] {m₃ m₄ : Raw α} (h₁ : m₁.WF) (h₂ : m₂.WF) (h₃ : m₃.WF) (h₄ : m₄.WF) (w₁ : m₁ ~m m₃) (w₂ : m₂ ~m m₄) : (m₁ == m₂) = (m₃ == m₄) :=
HashMap.Raw.Equiv.beq_congr h₁.1 h₂.1 h₃.1 h₄.1 w₁.1 w₂.1
end
section Union
variable (m₁ m₂ : Raw α)

View file

@ -257,6 +257,10 @@ end
| ⟨k, v⟩ :: l => if h : k == a then some (cast (congrArg β (eq_of_beq h)) v)
else getValueCast? a l
/-- Internal implementation detail of the hash map -/
def beqModel [BEq α] [LawfulBEq α] [∀ k, BEq (β k)] (l₁ l₂ : List ((a : α) × β a)) :=
if l₁.length ≠ l₂.length then false else (l₁.all fun x => getValueCast? x.fst l₂ == some x.snd)
@[simp] theorem getValueCast?_nil [BEq α] [LawfulBEq α] {a : α} :
getValueCast? a ([] : List ((a : α) × β a)) = none := rfl
theorem getValueCast?_cons [BEq α] [LawfulBEq α] {l : List ((a : α) × β a)} {k a : α} {v : β k} :
@ -285,6 +289,10 @@ section
variable {β : Type v}
/-- Internal implementation detail of the hash map -/
def Const.beqModel [BEq α] [BEq β] (l₁ l₂ : List ((_ : α) × β)) :=
if l₁.length ≠ l₂.length then false else (l₁.all fun x => getValue? x.fst l₂ == some x.snd)
/-- This is a strange dependent version of `Option.map` in which the mapping function is allowed to
"know" about the option that is being mapped. This happens to be useful in this file (see
`getValueCast_eq_getEntry?`), but we do not want it to leak out of the file. -/
@ -3569,6 +3577,71 @@ theorem insertList_insertEntry_right_equiv_insertEntry_insertList [BEq α] [Equi
. simp only [Option.some_or]
. rw [@getEntry?_insertList α β _ _ l toInsert distinct_l (DistinctKeys_impl_Pairwise_distinct distinct_toInsert) a]
theorem length_le_length_of_containsKey [BEq α] [EquivBEq α]
{l₁ l₂ : List ((a : α) × β a)}
(dl₁ : DistinctKeys l₁)
(dl₂ : DistinctKeys l₂)
(h : ∀ a, containsKey a l₁ → containsKey a l₂) :
l₁.length ≤ l₂.length := by
induction l₁ generalizing l₂ with
| nil => simp
| cons hd tl ih =>
have hc := h hd.1 (by rw [containsKey_cons_self])
suffices tl.length ≤ (eraseKey hd.1 l₂).length by
rw [List.length_eraseKey, hc] at this
simp only [↓reduceIte] at this
simp only [List.length_cons, ge_iff_le]
have : l₂.length > 0 := by cases l₂ <;> simp_all
omega
apply ih
· rw [List.distinctKeys_cons_iff] at dl₁
exact dl₁.1
· exact DistinctKeys.eraseKey dl₂
· intro a mem
rw [containsKey_eraseKey]
· simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true]
apply And.intro
· rw [List.distinctKeys_cons_iff] at dl₁
apply Classical.byContradiction
intro hyp
simp only [Bool.not_eq_false] at hyp
rw [containsKey_congr hyp] at dl₁
rw [dl₁.2] at mem
contradiction
· specialize h a
rw [containsKey_cons] at h
apply h
simp [mem]
· exact dl₂
theorem containsKey_of_length_eq [BEq α] [EquivBEq α] {l₁ l₂ : List ((a : α) × β a)} (dl₁ : DistinctKeys l₁) (dl₂ : DistinctKeys l₂) (hl : l₂.length = l₁.length) (hs : ∀ (a : α), containsKey a l₁ → containsKey a l₂) : ∀ (a : α), containsKey a l₂ → containsKey a l₁ := by
intro a ha
apply Classical.byContradiction
intro hb
simp only [Bool.not_eq_true] at hb
suffices l₁.length < l₂.length by omega
suffices l₁.length ≤ (eraseKey a l₂).length ∧ 1 + (eraseKey a l₂).length = l₂.length by omega
apply And.intro
· apply length_le_length_of_containsKey dl₁ (DistinctKeys.eraseKey dl₂)
intro a₂ mem₂
rw [containsKey_eraseKey dl₂]
simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true]
apply And.intro
· apply Classical.byContradiction
intro hyp
simp only [Bool.not_eq_false] at hyp
rw [containsKey_congr hyp] at hb
rw [hb] at mem₂
contradiction
· apply hs
exact mem₂
· simp only [length_eraseKey, ha, ↓reduceIte]
have : l₂.length > 0 := by
cases l₂ with
| nil => simp at ha
| _ => simp
omega
section
variable {β : Type v}
@ -7557,6 +7630,85 @@ theorem isEmpty_filter_key_iff [BEq α] [EquivBEq α] {f : α → Bool}
simp only [getKey, getKey?_eq_getEntry?, this] at h
exact h
theorem beqModel_eq_true_of_perm [BEq α] [LawfulBEq α] [∀ k, BEq (β k)] [∀ k, ReflBEq (β k)] {l₁ l₂ : List ((a : α) × β a)} (hl₁ : DistinctKeys l₁) : l₁.Perm l₂ → beqModel l₁ l₂ := by
intro hyp
rw [beqModel, if_neg (by simpa using hyp.length_eq)]
simp only [List.all_eq_true]
intro ⟨k,v⟩ mem
have hv := getValueCast_of_mem mem
have hc := containsKey_of_mem mem
apply beq_of_eq
simp only at |- hc hv
rw [← hv, ← getValueCast?_eq_some_getValueCast]
· symm
apply getValueCast?_of_perm hl₁ hyp
· exact hl₁
theorem Const.beqModel_eq_true_of_perm {β : Type v} [BEq α] [EquivBEq α] [BEq β] [ReflBEq β] {l₁ l₂ : List ((_ : α) × β )} (hl₁ : DistinctKeys l₁) : l₁.Perm l₂ → Const.beqModel l₁ l₂ := by
intro hyp
rw [beqModel, if_neg (by simpa using hyp.length_eq)]
simp only [List.all_eq_true]
intro ⟨k,v⟩ mem
have hv := @getValue_of_mem α β _ _ l₁ ⟨k,v⟩ mem
have hc := containsKey_of_mem mem
apply beq_of_eq
simp only at |- hc hv
rw [← hv, ← getValue?_eq_some_getValue]
· symm
apply getValue?_of_perm hl₁ hyp
· exact hl₁
· exact hc
theorem perm_of_beqModel [BEq α] [LawfulBEq α] [∀ k, BEq (β k)] [∀ k, LawfulBEq (β k)] {l₁ l₂ : List ((a : α) × β a)} (hl₁ : DistinctKeys l₁) (hl₂ : DistinctKeys l₂) : beqModel l₁ l₂ → l₁.Perm l₂ := by
simp only [beqModel, ne_eq, ite_not, Bool.if_false_right, Bool.and_eq_true, decide_eq_true_eq,
List.all_eq_true, beq_iff_eq, and_imp]
intro he hyp
apply getValueCast?_ext hl₁ hl₂
intro a
have hyp2 : ∀ (a : α) (h : containsKey a l₁), (getValueCast? a l₂ == getValueCast? a l₁) = true := by
intro a mem
rw [getValueCast?_eq_some_getValueCast mem]
apply beq_of_eq
specialize hyp ⟨a, getValueCast a l₁ mem⟩ (List.getValueCast_mem mem)
simpa using hyp
by_cases hc₁ : containsKey a l₁
case pos =>
exact eq_of_beq <| BEq.symm <| hyp2 _ hc₁
case neg =>
rw [Bool.not_eq_true] at hc₁
by_cases hc₂ : containsKey a l₂
case pos =>
suffices (∀ (a : α), containsKey a l₁ = true → containsKey a l₂ = true) by
rw [containsKey_of_length_eq hl₁ hl₂ he.symm this a hc₂] at hc₁
contradiction
intro k' mem
apply List.containsKey_of_getValueCast?_eq_some
simpa [getValueCast?_eq_some_getValueCast mem] using eq_of_beq <| hyp2 k' mem
case neg =>
rw [Bool.not_eq_true] at hc₂
rw [getValueCast?_eq_none hc₁, getValueCast?_eq_none hc₂]
theorem beqModel_congr [BEq α] [LawfulBEq α] [∀ k, BEq (β k)] {l₁ l₂ l₃ l₄ : List ((a : α) × β a)}
(hl : DistinctKeys l₂) (p₁ : l₁.Perm l₃) (p₂ : l₂.Perm l₄) : beqModel l₁ l₂ = beqModel l₃ l₄ := by
simp only [beqModel, ne_eq, ite_not, Bool.if_false_right, Perm.length_eq p₁, Perm.length_eq p₂]
suffices h : (l₁.all fun x => getValueCast? x.fst l₂ == some x.snd) = (l₃.all fun x => getValueCast? x.fst l₄ == some x.snd) by simp [h]
simp [getValueCast?_of_perm hl p₂, List.Perm.all_eq p₁]
theorem Const.beqModel_congr {β : Type v} [BEq α] [EquivBEq α] [BEq β] {l₁ l₂ l₃ l₄ : List ((_ : α) × β)}
(hl : DistinctKeys l₂) (p₁ : l₁.Perm l₃) (p₂ : l₂.Perm l₄) : beqModel l₁ l₂ = beqModel l₃ l₄ := by
simp only [beqModel, ne_eq, ite_not, Bool.if_false_right, Perm.length_eq p₁, Perm.length_eq p₂]
suffices h : (l₁.all fun x => getValue? x.fst l₂ == some x.snd) = (l₃.all fun x => getValue? x.fst l₄ == some x.snd) by simp [h]
simp [getValue?_of_perm hl p₂, List.Perm.all_eq p₁]
theorem beqModel_eq_constBeqModel {β : Type v} [BEq α] [LawfulBEq α] [BEq β] {l₁ l₂ : List ((_ : α) × β)} : beqModel l₁ l₂ = Const.beqModel l₁ l₂ := by
simp [beqModel, Const.beqModel, getValue?_eq_getValueCast?]
theorem Const.perm_of_beqModel {β : Type v} [BEq α] [LawfulBEq α] [BEq β] [LawfulBEq β] {l₁ l₂ : List ((_ : α) × β)} (hl₁ : DistinctKeys l₁) (hl₂ : DistinctKeys l₂) :
beqModel l₁ l₂ → l₁.Perm l₂ := by
rw [← beqModel_eq_constBeqModel]
intro hyp
apply List.perm_of_beqModel hl₁ hl₂ hyp
namespace Const
theorem getKey_getValue_mem [BEq α] [EquivBEq α] {β : Type v} {l : List ((_ : α) × β)} {k : α} {h} :