From 747ea853b5936166b59b82d2e3057395544114aa Mon Sep 17 00:00:00 2001 From: Rob23oba <152706811+Rob23oba@users.noreply.github.com> Date: Mon, 28 Apr 2025 08:48:25 +0200 Subject: [PATCH] feat: extensional hash maps (#8004) This PR adds extensional hash maps and hash sets under the names `Std.ExtDHashMap`, `Std.ExtHashMap` and `Std.ExtHashSet`. Extensional hash maps work like regular hash maps, except that they have extensionality lemmas which make them easier to use in proofs. This however makes it also impossible to regularly iterate over its entries. --- src/Init/Core.lean | 59 +- src/Std/Data.lean | 3 + src/Std/Data/DHashMap/Basic.lean | 9 +- src/Std/Data/DHashMap/Internal/RawLemmas.lean | 121 +- src/Std/Data/DHashMap/Lemmas.lean | 94 +- src/Std/Data/DHashMap/Raw.lean | 6 +- src/Std/Data/DHashMap/RawLemmas.lean | 101 +- src/Std/Data/ExtDHashMap.lean | 8 + src/Std/Data/ExtDHashMap/Basic.lean | 337 ++ src/Std/Data/ExtDHashMap/Lemmas.lean | 3289 +++++++++++++++++ src/Std/Data/ExtHashMap.lean | 8 + src/Std/Data/ExtHashMap/Basic.lean | 248 ++ src/Std/Data/ExtHashMap/Lemmas.lean | 2116 +++++++++++ src/Std/Data/ExtHashSet.lean | 8 + src/Std/Data/ExtHashSet/Basic.lean | 202 + src/Std/Data/ExtHashSet/Lemmas.lean | 694 ++++ src/Std/Data/HashMap/Basic.lean | 6 +- src/Std/Data/HashMap/Lemmas.lean | 59 +- src/Std/Data/HashMap/Raw.lean | 10 +- src/Std/Data/HashMap/RawLemmas.lean | 60 +- src/Std/Data/HashSet/Basic.lean | 4 +- src/Std/Data/HashSet/Lemmas.lean | 28 +- src/Std/Data/HashSet/Raw.lean | 10 +- src/Std/Data/HashSet/RawLemmas.lean | 28 +- src/Std/Data/Internal/List/Associative.lean | 28 +- 25 files changed, 7477 insertions(+), 59 deletions(-) create mode 100644 src/Std/Data/ExtDHashMap.lean create mode 100644 src/Std/Data/ExtDHashMap/Basic.lean create mode 100644 src/Std/Data/ExtDHashMap/Lemmas.lean create mode 100644 src/Std/Data/ExtHashMap.lean create mode 100644 src/Std/Data/ExtHashMap/Basic.lean create mode 100644 src/Std/Data/ExtHashMap/Lemmas.lean create mode 100644 src/Std/Data/ExtHashSet.lean create mode 100644 src/Std/Data/ExtHashSet/Basic.lean create mode 100644 src/Std/Data/ExtHashSet/Lemmas.lean diff --git a/src/Init/Core.lean b/src/Init/Core.lean index 797e1b024f..ee0b64d603 100644 --- a/src/Init/Core.lean +++ b/src/Init/Core.lean @@ -938,6 +938,34 @@ term. theorem eqRec_heq {α : Sort u} {φ : α → Sort v} {a a' : α} : (h : a = a') → (p : φ a) → HEq (Eq.recOn (motive := fun x _ => φ x) h p) p | rfl, p => HEq.refl p +/-- +Heterogenous equality with an `Eq.rec` application on the left is equivalent to a heterogenous +equality on the original term. +-/ +theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) → a = b → Sort v} + {b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} : + HEq (@Eq.rec α a motive refl b h) c ↔ HEq refl c := + h.rec (fun _ => ⟨id, id⟩) c + +/-- +Heterogenous equality with an `Eq.rec` application on the right is equivalent to a heterogenous +equality on the original term. +-/ +theorem heq_eqRec_iff {α : Sort u} {a : α} {motive : (b : α) → a = b → Sort v} + {b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} : + HEq c (@Eq.rec α a motive refl b h) ↔ HEq c refl := + h.rec (fun _ => ⟨id, id⟩) c + +/-- +Moves an cast using `Eq.rec` from the function to the argument. +Note: because the motive isn't reliably detected by unification, +it needs to be provided as an explicit parameter. +-/ +theorem apply_eqRec {α : Sort u} {a : α} (motive : (b : α) → a = b → Sort v) + {b : α} {h : a = b} {c : motive a (Eq.refl a) → β} {d : motive b h} : + @Eq.rec α a (fun b h => motive b h → β) c b h d = c (h.symm ▸ d) := by + cases h; rfl + /-- If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two terms are heterogeneously equal. @@ -983,7 +1011,7 @@ theorem HEq.comm {a : α} {b : β} : HEq a b ↔ HEq b a := Iff.intro HEq.symm H theorem heq_comm {a : α} {b : β} : HEq a b ↔ HEq b a := HEq.comm @[symm] theorem Iff.symm (h : a ↔ b) : b ↔ a := Iff.intro h.mpr h.mp -theorem Iff.comm: (a ↔ b) ↔ (b ↔ a) := Iff.intro Iff.symm Iff.symm +theorem Iff.comm : (a ↔ b) ↔ (b ↔ a) := Iff.intro Iff.symm Iff.symm theorem iff_comm : (a ↔ b) ↔ (b ↔ a) := Iff.comm @[symm] theorem And.symm : a ∧ b → b ∧ a := fun ⟨ha, hb⟩ => ⟨hb, ha⟩ @@ -1148,12 +1176,12 @@ theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) : | isTrue _ => rfl | isFalse _ => rfl -instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) := +instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) := match dC with | isTrue _ => dT | isFalse _ => dE -instance {c : Prop} {t : c → Prop} {e : ¬c → Prop} [dC : Decidable c] [dT : ∀ h, Decidable (t h)] [dE : ∀ h, Decidable (e h)] : Decidable (if h : c then t h else e h) := +instance {c : Prop} {t : c → Prop} {e : ¬c → Prop} [dC : Decidable c] [dT : ∀ h, Decidable (t h)] [dE : ∀ h, Decidable (e h)] : Decidable (if h : c then t h else e h) := match dC with | isTrue hc => dT hc | isFalse hc => dE hc @@ -1869,9 +1897,7 @@ protected abbrev hrecOn (f : (a : α) → motive (Quot.mk r a)) (c : (a b : α) → (p : r a b) → HEq (f a) (f b)) : motive q := - Quot.recOn q f fun a b p => eq_of_heq <| - have p₁ : HEq (Eq.ndrec (f a) (sound p)) (f a) := eqRec_heq (sound p) (f a) - HEq.trans p₁ (c a b p) + Quot.recOn q f fun a b p => eq_of_heq (eqRec_heq_iff.mpr (c a b p)) end end Quot @@ -2234,6 +2260,27 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x} show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g) exact congrArg extfunApp (Quot.sound h) +/-- +Like `Quot.liftOn q f h` but allows `f a` to "know" that `q = Quot.mk r a`. +-/ +protected abbrev Quot.pliftOn {α : Sort u} {r : α → α → Prop} + (q : Quot r) + (f : (a : α) → q = Quot.mk r a → β) + (h : ∀ (a b : α) (h h'), r a b → f a h = f b h') : β := + q.rec (motive := fun q' => q = q' → β) f + (fun a b p => funext fun h' => + (apply_eqRec (motive := fun b _ => q = b)).trans + (@h a b (h'.trans (sound p).symm) h' p)) rfl + +/-- +Like `Quotient.liftOn q f h` but allows `f a` to "know" that `q = Quotient.mk s a`. +-/ +protected abbrev Quotient.pliftOn {α : Sort u} {s : Setoid α} + (q : Quotient s) + (f : (a : α) → q = Quotient.mk s a → β) + (h : ∀ (a b : α) (h h'), a ≈ b → f a h = f b h') : β := + Quot.pliftOn q f h + instance Pi.instSubsingleton {α : Sort u} {β : α → Sort v} [∀ a, Subsingleton (β a)] : Subsingleton (∀ a, β a) where allEq f g := funext fun a => Subsingleton.elim (f a) (g a) diff --git a/src/Std/Data.lean b/src/Std/Data.lean index 0b27735c58..38b090df67 100644 --- a/src/Std/Data.lean +++ b/src/Std/Data.lean @@ -10,6 +10,9 @@ import Std.Data.HashSet import Std.Data.DTreeMap import Std.Data.TreeMap import Std.Data.TreeSet +import Std.Data.ExtDHashMap +import Std.Data.ExtHashMap +import Std.Data.ExtHashSet -- The imports above only import the modules needed to work with the version which bundles -- the well-formedness invariant, so we need to additionally import the files that deal with the diff --git a/src/Std/Data/DHashMap/Basic.lean b/src/Std/Data/DHashMap/Basic.lean index 8a5997f3e6..e10e1468b2 100644 --- a/src/Std/Data/DHashMap/Basic.lean +++ b/src/Std/Data/DHashMap/Basic.lean @@ -18,7 +18,7 @@ Lemmas about the operations on `Std.Data.DHashMap` are available in the module `Std.Data.DHashMap.Lemmas`. See the module `Std.Data.DHashMap.Raw` for a variant of this type which is safe to use in -nested inductive types. +nested inductive types and the module `Std.Data.ExtDHashMap` for a variant with extensionality. For implementation notes, see the docstring of the module `Std.Data.DHashMap.Internal.Defs`. -/ @@ -54,9 +54,12 @@ should be an equivalence relation and `a == b` should imply `hash a = hash b` (s instance is lawful, i.e., if `a == b` implies `a = b`. These hash maps contain a bundled well-formedness invariant, which means that they cannot -be used in nested inductive types. For these use cases, `Std.Data.DHashMap.Raw` and -`Std.Data.DHashMap.Raw.WF` unbundle the invariant from the hash map. When in doubt, prefer +be used in nested inductive types. For these use cases, `Std.DHashMap.Raw` and +`Std.DHashMap.Raw.WF` unbundle the invariant from the hash map. When in doubt, prefer `DHashMap` over `DHashMap.Raw`. + +For a variant that is more convenient for use in proofs because of extensionalities, see +`Std.ExtDHashMap` which is defined in the module `Std.Data.ExtDHashMap`. -/ def DHashMap (α : Type u) (β : α → Type v) [BEq α] [Hashable α] := { m : DHashMap.Raw α β // m.WF } diff --git a/src/Std/Data/DHashMap/Internal/RawLemmas.lean b/src/Std/Data/DHashMap/Internal/RawLemmas.lean index e2ba6f36b2..18a4710e07 100644 --- a/src/Std/Data/DHashMap/Internal/RawLemmas.lean +++ b/src/Std/Data/DHashMap/Internal/RawLemmas.lean @@ -1178,8 +1178,6 @@ theorem foldRevM_eq_foldrM_toList [Monad m'] [LawfulMonad m'] {f : δ → α → β → m' δ} {init : δ} : Raw.Internal.foldRevM f init m.1 = (Raw.Const.toList m.1).foldrM (fun a b => f b a.1 a.2) init := by - have :=Raw.foldRevM_eq_foldrM_toListModel (m := m') (b := m.1) (init := init) (f := f) - simp_to_model [foldRevM, Const.toList] using List.foldrM_eq_foldrM_toProd' theorem foldRev_eq_foldr_toList {f : δ → α → β → δ} {init : δ} : @@ -1199,6 +1197,16 @@ end Const end monadic +section insertMany + +variable {ρ : Type w} [ForIn Id ρ ((a : α) × β a)] + +@[elab_as_elim] +theorem insertMany_ind {motive : Raw₀ α β → Prop} (m : Raw₀ α β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l).1 := + (m.insertMany l).2 motive (insert _ _ _) init + @[simp] theorem insertMany_nil : m.insertMany [] = m := by @@ -1227,6 +1235,14 @@ theorem contains_of_contains_insertMany_list [EquivBEq α] [LawfulHashable α] ( (m.insertMany l).1.contains k → (l.map Sigma.fst).contains k = false → m.contains k := by simp_to_model [insertMany, contains] using List.containsKey_of_containsKey_insertList +theorem contains_insertMany_of_contains [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} {k : α} (h' : m.contains k) : (m.insertMany l).1.contains k := by + refine (?_ : _ ∧ (m.insertMany l).1.1.WF).1 + refine insertMany_ind m l ⟨h', h⟩ ?_ + intro m a b ⟨h', h⟩ + simp only [h, contains_insert, h', Bool.or_true, true_and] + exact h.insert₀ + theorem get?_insertMany_list_of_contains_eq_false [LawfulBEq α] (h : m.1.WF) {l : List ((a : α) × β a)} {k : α} (h' : (l.map Sigma.fst).contains k = false) : @@ -1352,6 +1368,15 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.1. m.1.size ≤ (m.insertMany l).1.1.size := by simp_to_model [insertMany, size] using List.length_le_length_insertList +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} : m.1.size ≤ (m.insertMany l).1.1.size := by + refine (?_ : _ ∧ (m.insertMany l).1.1.WF).1 + refine insertMany_ind m l ⟨Nat.le_refl _, h⟩ ?_ + intro m' a b ⟨h', h⟩ + constructor + · exact Nat.le_trans h' (size_le_size_insert m' h) + · exact h.insert₀ + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List ((a : α) × β a)} : (m.insertMany l).1.1.size ≤ m.1.size + l.length := by @@ -1363,9 +1388,26 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.1.WF) (m.insertMany l).1.1.isEmpty = (m.1.isEmpty && l.isEmpty) := by simp_to_model [insertMany, isEmpty] using List.isEmpty_insertList +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} : (m.insertMany l).1.1.isEmpty → m.1.isEmpty := by + refine (?_ : _ ∧ (m.insertMany l).1.1.WF).1 + refine insertMany_ind m l ⟨id, h⟩ ?_ + intro m' a b ⟨h', h⟩ + constructor + · intro h'' + simp only [isEmpty_insert, h, Bool.false_eq_true] at h'' + · exact h.insert₀ + namespace Const variable {β : Type v} (m : Raw₀ α (fun _ => β)) +variable {ρ : Type w} [ForIn Id ρ (α × β)] + +@[elab_as_elim] +theorem insertMany_ind {motive : Raw₀ α (fun _ => β) → Prop} (m : Raw₀ α fun _ => β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (insertMany m l).1 := + (insertMany m l).2 motive (insert _ _ _) init @[simp] theorem insertMany_nil : @@ -1395,6 +1437,14 @@ theorem contains_of_contains_insertMany_list [EquivBEq α] [LawfulHashable α] ( (insertMany m l).1.contains k → (l.map Prod.fst).contains k = false → m.contains k := by simp_to_model [Const.insertMany, contains] using List.containsKey_of_containsKey_insertListConst +theorem contains_insertMany_of_contains [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} {k : α} (h' : m.contains k) : (insertMany m l).1.contains k := by + refine (?_ : _ ∧ (insertMany m l).1.1.WF).1 + refine insertMany_ind m l ⟨h', h⟩ ?_ + intro m a b ⟨h', h⟩ + simp only [h, contains_insert, h', Bool.or_true, true_and] + exact h.insert₀ + theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List (α × β)} {k : α} (h' : (l.map Prod.fst).contains k = false) : @@ -1466,6 +1516,15 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.1. m.1.size ≤ (insertMany m l).1.1.size := by simp_to_model [Const.insertMany, size] using List.length_le_length_insertListConst +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} : m.1.size ≤ (insertMany m l).1.1.size := by + refine (?_ : _ ∧ (insertMany m l).1.1.WF).1 + refine insertMany_ind m l ⟨Nat.le_refl _, h⟩ ?_ + intro m' a b ⟨h', h⟩ + constructor + · exact Nat.le_trans h' (size_le_size_insert m' h) + · exact h.insert₀ + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List (α × β)} : (insertMany m l).1.1.size ≤ m.1.size + l.length := by @@ -1477,6 +1536,16 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.1.WF) (insertMany m l).1.1.isEmpty = (m.1.isEmpty && l.isEmpty) := by simp_to_model [Const.insertMany, isEmpty] using List.isEmpty_insertListConst +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} : (insertMany m l).1.1.isEmpty → m.1.isEmpty := by + refine (?_ : _ ∧ (insertMany m l).1.1.WF).1 + refine insertMany_ind m l ⟨id, h⟩ ?_ + intro m' a b ⟨h', h⟩ + constructor + · intro h'' + simp only [isEmpty_insert, h, Bool.false_eq_true] at h'' + · exact h.insert₀ + theorem get?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List (α × β)} {k : α} (h' : (l.map Prod.fst).contains k = false) : @@ -1532,6 +1601,15 @@ theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] (h : m.1.W variable (m : Raw₀ α (fun _ => Unit)) +variable {ρ : Type w} [ForIn Id ρ α] + +@[elab_as_elim] +theorem insertManyIfNewUnit_ind {motive : Raw₀ α (fun _ => Unit) → Prop} + (m : Raw₀ α fun _ => Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l).1 := + (insertManyIfNewUnit m l).2 motive (insert _ _) init + @[simp] theorem insertManyIfNewUnit_nil : insertManyIfNewUnit m [] = m := by @@ -1561,6 +1639,14 @@ theorem contains_of_contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHasha simp_to_model [Const.insertManyIfNewUnit, contains] using List.containsKey_of_containsKey_insertListIfNewUnit +theorem contains_insertManyIfNewUnit_of_contains [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} {k : α} (h' : m.contains k) : (insertManyIfNewUnit m l).1.contains k := by + refine (?_ : _ ∧ (insertManyIfNewUnit m l).1.1.WF).1 + refine insertManyIfNewUnit_ind m l ⟨h', h⟩ ?_ + intro m a ⟨h', h⟩ + simp only [h, contains_insertIfNew, h', Bool.or_true, true_and] + exact h.insertIfNew₀ + theorem getKey?_insertManyIfNewUnit_list_of_contains_eq_false_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List α} {k : α} : @@ -1652,6 +1738,15 @@ theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] m.1.size ≤ (insertManyIfNewUnit m l).1.1.size := by simp_to_model [Const.insertManyIfNewUnit, size] using List.length_le_length_insertListIfNewUnit +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} : m.1.size ≤ (insertManyIfNewUnit m l).1.1.size := by + refine (?_ : _ ∧ (insertManyIfNewUnit m l).1.1.WF).1 + refine insertManyIfNewUnit_ind m l ⟨Nat.le_refl _, h⟩ ?_ + intro m' a ⟨h', h⟩ + constructor + · exact Nat.le_trans h' (size_le_size_insertIfNew m' h) + · exact h.insertIfNew₀ + theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List α} : (insertManyIfNewUnit m l).1.1.size ≤ m.1.size + l.length := by @@ -1663,6 +1758,16 @@ theorem isEmpty_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : (insertManyIfNewUnit m l).1.1.isEmpty = (m.1.isEmpty && l.isEmpty) := by simp_to_model [Const.insertManyIfNewUnit, isEmpty] using List.isEmpty_insertListIfNewUnit +theorem isEmpty_of_isEmpty_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] (h : m.1.WF) + {l : ρ} : (insertManyIfNewUnit m l).1.1.isEmpty → m.1.isEmpty := by + refine (?_ : _ ∧ (insertManyIfNewUnit m l).1.1.WF).1 + refine insertManyIfNewUnit_ind m l ⟨id, h⟩ ?_ + intro m' a ⟨h', h⟩ + constructor + · intro h'' + simp only [isEmpty_insertIfNew, h, Bool.false_eq_true] at h'' + · exact h.insertIfNew₀ + theorem get?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : m.1.WF) {l : List α} {k : α} : get? (insertManyIfNewUnit m l).1 k = @@ -2329,6 +2434,8 @@ abbrev getD_insertManyIfNewUnit_empty_list := @getD_insertManyIfNewUnit_emptyWit end Const +end insertMany + section Alter theorem isEmpty_alter_eq_isEmpty_erase [LawfulBEq α] (h : m.1.WF) {k : α} @@ -3069,10 +3176,20 @@ theorem modify_equiv_congr (h₁ : m₁.1.WF) (h₂ : m₂.1.WF) (h : m₁.1 ~m {k : α} (f : β → β) : (modify m₁ k f).1 ~m (modify m₂ k f).1 := by simp_to_model [Equiv, Const.modify] using List.Const.modifyKey_of_perm _ h.1 +theorem equiv_of_forall_getKey_eq_of_forall_get?_eq (h₁ : m₁.1.WF) (h₂ : m₂.1.WF) : + (∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') → (∀ k, get? m₁ k = get? m₂ k) → m₁.1 ~m m₂.1 := by + simp_to_model [getKey, Const.get?, contains, Equiv] using List.getKey_getValue?_ext + +@[deprecated equiv_of_forall_getKey_eq_of_forall_get?_eq (since := "2025-04-25")] theorem equiv_of_forall_getKey?_eq_of_forall_get?_eq (h₁ : m₁.1.WF) (h₂ : m₂.1.WF) : (∀ k, m₁.getKey? k = m₂.getKey? k) → (∀ k, get? m₁ k = get? m₂ k) → m₁.1 ~m m₂.1 := by simp_to_model [getKey?, Const.get?, Equiv] using List.getKey?_getValue?_ext +theorem equiv_of_forall_get?_eq {α : Type u} [BEq α] [Hashable α] [LawfulBEq α] + {m₁ m₂ : Raw₀ α fun _ => β} (h₁ : m₁.1.WF) (h₂ : m₂.1.WF) : + (∀ k, get? m₁ k = get? m₂ k) → m₁.1 ~m m₂.1 := by + simpa only [Const.get?_eq_get?, h₁, h₂] using Raw₀.equiv_of_forall_get?_eq m₁ m₂ h₁ h₂ + theorem equiv_of_forall_getKey?_unit_eq {m₁ m₂ : Raw₀ α fun _ => Unit} (h₁ : m₁.1.WF) (h₂ : m₂.1.WF) : (∀ k, m₁.getKey? k = m₂.getKey? k) → m₁.1 ~m m₂.1 := by simp_to_model [getKey?, Equiv] using List.getKey?_ext diff --git a/src/Std/Data/DHashMap/Lemmas.lean b/src/Std/Data/DHashMap/Lemmas.lean index a1abfb9a2f..209ee68d56 100644 --- a/src/Std/Data/DHashMap/Lemmas.lean +++ b/src/Std/Data/DHashMap/Lemmas.lean @@ -11,7 +11,7 @@ import Std.Data.DHashMap.AdditionalOperations /-! # Dependent hash map lemmas -This file contains lemmas about `Std.Data.DHashMap`. Most of the lemmas require +This file contains lemmas about `Std.DHashMap`. Most of the lemmas require `EquivBEq α` and `LawfulHashable α` for the key type `α`. The easiest way to obtain these instances is to provide an instance of `LawfulBEq α`. -/ @@ -1345,6 +1345,8 @@ end Const end monadic +variable {ρ : Type w} [ForIn Id ρ ((a : α) × β a)] + @[simp] theorem insertMany_nil : m.insertMany [] = m := @@ -1359,6 +1361,14 @@ theorem insertMany_cons {l : List ((a : α) × β a)} {k : α} {v : β k} : m.insertMany (⟨k, v⟩ :: l) = (m.insert k v).insertMany l := Subtype.eq (congrArg Subtype.val (Raw₀.insertMany_cons ⟨m.1, m.2.size_buckets_pos⟩) :) +@[elab_as_elim] +theorem insertMany_ind {motive : DHashMap α β → Prop} (m : DHashMap α β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l) := + (Raw₀.insertMany_ind ⟨m.1, _⟩ l ⟨m.2, init⟩ + (fun m a b ⟨h, h'⟩ => ⟨h.insert₀, insert ⟨m, h⟩ a b h'⟩) : + ∃ h, motive ⟨(Raw₀.insertMany _ l).1, h⟩).2 + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] {l : List ((a : α) × β a)} {k : α} : @@ -1377,6 +1387,10 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] k ∈ m := Raw₀.contains_of_contains_insertMany_list ⟨m.1, _⟩ m.2 mem contains_eq_false +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} (h : k ∈ m) : k ∈ (m.insertMany l) := + Raw₀.contains_insertMany_of_contains ⟨m.1, _⟩ m.2 h + theorem get?_insertMany_list_of_contains_eq_false [LawfulBEq α] {l : List ((a : α) × β a)} {k : α} (contains_eq_false : (l.map Sigma.fst).contains k = false) : @@ -1502,6 +1516,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] m.size ≤ (m.insertMany l).size := Raw₀.size_le_size_insertMany_list ⟨m.1, _⟩ m.2 +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (m.insertMany l).size := + Raw₀.size_le_size_insertMany ⟨m.1, _⟩ m.2 + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] {l : List ((a : α) × β a)} : (m.insertMany l).size ≤ m.size + l.length := @@ -1513,9 +1531,14 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (m.insertMany l).isEmpty = (m.isEmpty && l.isEmpty) := Raw₀.isEmpty_insertMany_list ⟨m.1, _⟩ m.2 +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : (m.insertMany l).isEmpty → m.isEmpty := + Raw₀.isEmpty_of_isEmpty_insertMany ⟨m.1, _⟩ m.2 + namespace Const variable {β : Type v} {m : DHashMap α (fun _ => β)} +variable {ρ : Type w} [ForIn Id ρ (α × β)] @[simp] theorem insertMany_nil : @@ -1532,6 +1555,14 @@ theorem insertMany_cons {l : List (α × β)} {k : α} {v : β} : insertMany m (⟨k, v⟩ :: l) = insertMany (m.insert k v) l := Subtype.eq (congrArg Subtype.val (Raw₀.Const.insertMany_cons ⟨m.1, m.2.size_buckets_pos⟩) :) +@[elab_as_elim] +theorem insertMany_ind {motive : DHashMap α (fun _ => β) → Prop} (m : DHashMap α fun _ => β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (insertMany m l) := + (Raw₀.Const.insertMany_ind ⟨m.1, _⟩ l ⟨m.2, init⟩ + (fun m a b ⟨h, h'⟩ => ⟨h.insert₀, insert ⟨m, h⟩ a b h'⟩) : + ∃ h, motive ⟨(Raw₀.Const.insertMany _ l).1, h⟩).2 + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {k : α} : @@ -1550,6 +1581,10 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] k ∈ m := Raw₀.Const.contains_of_contains_insertMany_list ⟨m.1, _⟩ m.2 mem contains_eq_false +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} (h : k ∈ m) : k ∈ insertMany m l := + Raw₀.Const.contains_insertMany_of_contains ⟨m.1, _⟩ m.2 h + theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1621,6 +1656,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertMany m l).size := Raw₀.Const.size_le_size_insertMany_list ⟨m.1, _⟩ m.2 +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertMany m l).size := + Raw₀.Const.size_le_size_insertMany ⟨m.1, _⟩ m.2 + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] {l : List (α × β)} : (insertMany m l).size ≤ m.size + l.length := @@ -1632,6 +1671,10 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (insertMany m l).isEmpty = (m.isEmpty && l.isEmpty) := Raw₀.Const.isEmpty_insertMany_list ⟨m.1, _⟩ m.2 +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : (insertMany m l).isEmpty → m.isEmpty := + Raw₀.Const.isEmpty_of_isEmpty_insertMany ⟨m.1, _⟩ m.2 + theorem get?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1682,6 +1725,7 @@ theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] Raw₀.Const.getD_insertMany_list_of_mem ⟨m.1, _⟩ m.2 k_beq distinct mem variable {m : DHashMap α (fun _ => Unit)} +variable {ρ : Type w} [ForIn Id ρ α] @[simp] theorem insertManyIfNewUnit_nil : @@ -1700,6 +1744,15 @@ theorem insertManyIfNewUnit_cons {l : List α} {k : α} : Subtype.eq (congrArg Subtype.val (Raw₀.Const.insertManyIfNewUnit_cons ⟨m.1, m.2.size_buckets_pos⟩) :) +@[elab_as_elim] +theorem insertManyIfNewUnit_ind {motive : DHashMap α (fun _ => Unit) → Prop} + (m : DHashMap α fun _ => Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l) := + (Raw₀.Const.insertManyIfNewUnit_ind ⟨m.1, _⟩ l ⟨m.2, init⟩ + (fun m a ⟨h, h'⟩ => ⟨h.insertIfNew₀, insert ⟨m, h⟩ a h'⟩) : + ∃ h, motive ⟨(Raw₀.Const.insertManyIfNewUnit _ l).1, h⟩).2 + @[simp] theorem contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : @@ -1717,6 +1770,10 @@ theorem mem_of_mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] k ∈ insertManyIfNewUnit m l → k ∈ m := Raw₀.Const.contains_of_contains_insertManyIfNewUnit_list ⟨m.1, _⟩ m.2 contains_eq_false +theorem mem_insertManyIfNewUnit_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} (h : k ∈ m) : k ∈ insertManyIfNewUnit m l := + Raw₀.Const.contains_insertManyIfNewUnit_of_contains ⟨m.1, _⟩ m.2 h + theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : @@ -1810,6 +1867,10 @@ theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertManyIfNewUnit m l).size := Raw₀.Const.size_le_size_insertManyIfNewUnit_list ⟨m.1, _⟩ m.2 +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertManyIfNewUnit m l).size := + Raw₀.Const.size_le_size_insertManyIfNewUnit ⟨m.1, _⟩ m.2 + theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] {l : List α} : (insertManyIfNewUnit m l).size ≤ m.size + l.length := @@ -1821,6 +1882,10 @@ theorem isEmpty_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (insertManyIfNewUnit m l).isEmpty = (m.isEmpty && l.isEmpty) := Raw₀.Const.isEmpty_insertManyIfNewUnit_list ⟨m.1, _⟩ m.2 +theorem isEmpty_of_isEmpty_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {l : ρ} : (insertManyIfNewUnit m l).isEmpty → m.isEmpty := + Raw₀.Const.isEmpty_of_isEmpty_insertManyIfNewUnit ⟨m.1, _⟩ m.2 + theorem get?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : get? (insertManyIfNewUnit m l) k = @@ -2916,12 +2981,15 @@ namespace Equiv variable {m₁ m₂ m₃ : Std.DHashMap α β} -theorem refl (m : Std.DHashMap α β) : m ~m m := ⟨⟨.rfl⟩⟩ +@[refl, simp] theorem refl (m : Std.DHashMap α β) : m ~m m := ⟨⟨.rfl⟩⟩ theorem rfl : m ~m m := ⟨⟨.rfl⟩⟩ -theorem symm : m₁ ~m m₂ → m₂ ~m m₁ +@[symm] theorem symm : m₁ ~m m₂ → m₂ ~m m₁ | ⟨⟨h⟩⟩ => ⟨⟨h.symm⟩⟩ theorem trans : m₁ ~m m₂ → m₂ ~m m₃ → m₁ ~m m₃ | ⟨⟨h₁⟩⟩, ⟨⟨h₂⟩⟩ => ⟨⟨h₁.trans h₂⟩⟩ + +instance instTrans : Trans (α := Std.DHashMap α β) Equiv Equiv Equiv := ⟨trans⟩ + theorem comm : m₁ ~m m₂ ↔ m₂ ~m m₁ := ⟨symm, symm⟩ theorem congr_left (h : m₁ ~m m₂) : m₁ ~m m₃ ↔ m₂ ~m m₃ := ⟨h.symm.trans, h.trans⟩ theorem congr_right (h : m₁ ~m m₂) : m₃ ~m m₁ ↔ m₃ ~m m₂ := @@ -3066,11 +3134,22 @@ theorem constModify [EquivBEq α] [LawfulHashable α] (k : α) (f : β → β) ( Const.modify m₁ k f ~m Const.modify m₂ k f := ⟨Raw₀.Const.modify_equiv_congr ⟨m₁.1, _⟩ ⟨m₂.1, _⟩ m₁.2 m₂.2 h.1 f⟩ +theorem of_forall_getKey_eq_of_forall_constGet?_eq [EquivBEq α] [LawfulHashable α] + (hk : ∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') (hv : ∀ k, Const.get? m₁ k = Const.get? m₂ k) : + m₁ ~m m₂ := + ⟨Raw₀.Const.equiv_of_forall_getKey_eq_of_forall_get?_eq ⟨m₁.1, _⟩ ⟨m₂.1, _⟩ m₁.2 m₂.2 hk hv⟩ + +set_option linter.deprecated false in +@[deprecated of_forall_getKey_eq_of_forall_constGet?_eq (since := "2025-04-25")] theorem of_forall_getKey?_eq_of_forall_constGet?_eq [EquivBEq α] [LawfulHashable α] (hk : ∀ k, m₁.getKey? k = m₂.getKey? k) (hv : ∀ k, Const.get? m₁ k = Const.get? m₂ k) : m₁ ~m m₂ := ⟨Raw₀.Const.equiv_of_forall_getKey?_eq_of_forall_get?_eq ⟨m₁.1, _⟩ ⟨m₂.1, _⟩ m₁.2 m₂.2 hk hv⟩ +theorem of_forall_constGet?_eq [LawfulBEq α] (hv : ∀ k, Const.get? m₁ k = Const.get? m₂ k) : + m₁ ~m m₂ := + ⟨Raw₀.Const.equiv_of_forall_get?_eq m₁.2 m₂.2 hv⟩ + theorem of_forall_getKey?_unit_eq [EquivBEq α] [LawfulHashable α] {m₁ m₂ : DHashMap α fun _ => Unit} (h : ∀ k, m₁.getKey? k = m₂.getKey? k) : m₁ ~m m₂ := ⟨Raw₀.Const.equiv_of_forall_getKey?_unit_eq m₁.2 m₂.2 h⟩ @@ -3087,6 +3166,15 @@ end Const end Equiv +/-- Internal implementation detail of the hash map. -/ +def isSetoid (α β) [BEq α] [Hashable α] : Setoid (DHashMap α β) where + r := Equiv + iseqv := { + refl := .refl + symm := .symm + trans := .trans + } + @[simp] theorem equiv_emptyWithCapacity_iff_isEmpty [EquivBEq α] [LawfulHashable α] {c : Nat} : m ~m emptyWithCapacity c ↔ m.isEmpty := diff --git a/src/Std/Data/DHashMap/Raw.lean b/src/Std/Data/DHashMap/Raw.lean index cbc918fbcb..e415eb1e8a 100644 --- a/src/Std/Data/DHashMap/Raw.lean +++ b/src/Std/Data/DHashMap/Raw.lean @@ -8,17 +8,17 @@ import Init.Data.BEq import Init.Data.Hashable import Std.Data.DHashMap.Internal.Defs -/- +/-! # Dependent hash maps with unbundled well-formedness invariant This file develops the type `Std.DHashMap.Raw` of dependent hash maps with unbundled well-formedness invariant. This version is safe to use in nested inductive types. The well-formedness predicate is -available as `Std.Data.DHashMap.Raw.WF` and we prove in this file that all operations preserve +available as `Std.DHashMap.Raw.WF` and we prove in this file that all operations preserve well-formedness. When in doubt, prefer `DHashMap` over `DHashMap.Raw`. -Lemmas about the operations on `Std.Data.DHashMap.Raw` are available in the module +Lemmas about the operations on `Std.DHashMap.Raw` are available in the module `Std.Data.DHashMap.RawLemmas`. -/ diff --git a/src/Std/Data/DHashMap/RawLemmas.lean b/src/Std/Data/DHashMap/RawLemmas.lean index bd9b9ce7a1..69aea52310 100644 --- a/src/Std/Data/DHashMap/RawLemmas.lean +++ b/src/Std/Data/DHashMap/RawLemmas.lean @@ -1438,6 +1438,10 @@ end Const end monadic +section insertMany + +variable {ρ : Type w} [ForIn Id ρ ((a : α) × β a)] + @[simp] theorem insertMany_nil [EquivBEq α] [LawfulHashable α] (h : m.WF) : m.insertMany [] = m := by @@ -1456,6 +1460,17 @@ theorem insertMany_cons {l : List ((a : α) × β a)} {k : α} {v : β k} [Equiv simp_to_raw rw [Raw₀.insertMany_cons] +@[elab_as_elim] +theorem insertMany_ind {motive : Raw α β → Prop} (m : Raw α β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l) := by + dsimp [insertMany] + split + · rename_i h + refine Raw₀.insertMany_ind ⟨m, h⟩ l init (fun m a b h => ?_) + simpa only [Raw.insert, m.2, ↓reduceDIte] using insert m.1 a b h + · exact init + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List ((a : α) × β a)} {k : α} : @@ -1474,6 +1489,11 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) simp only [mem_iff_contains] simp_to_raw using Raw₀.contains_of_contains_insertMany_list +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} {k : α} : k ∈ m → k ∈ m.insertMany l := by + simp only [mem_iff_contains] + simp_to_raw using Raw₀.contains_insertMany_of_contains + theorem get?_insertMany_list_of_contains_eq_false [LawfulBEq α] (h : m.WF) {l : List ((a : α) × β a)} {k : α} (contains_eq_false : (l.map Sigma.fst).contains k = false) : @@ -1600,6 +1620,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF m.size ≤ (m.insertMany l).size := by simp_to_raw using Raw₀.size_le_size_insertMany_list ⟨m, _⟩ +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : m.size ≤ (m.insertMany l).size := by + simp_to_raw using Raw₀.size_le_size_insertMany ⟨m, _⟩ + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List ((a : α) × β a)} : (m.insertMany l).size ≤ m.size + l.length := by @@ -1611,9 +1635,14 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) (m.insertMany l).isEmpty = (m.isEmpty && l.isEmpty) := by simp_to_raw using Raw₀.isEmpty_insertMany_list +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : (m.insertMany l).isEmpty → m.isEmpty := by + simp_to_raw using Raw₀.isEmpty_of_isEmpty_insertMany + namespace Const variable {β : Type v} {m : Raw α (fun _ => β)} +variable {ρ : Type w} [ForIn Id ρ (α × β)] @[simp] theorem insertMany_nil (h : m.WF) : @@ -1634,6 +1663,17 @@ theorem insertMany_cons (h : m.WF) {l : List (α × β)} simp_to_raw rw [Raw₀.Const.insertMany_cons] +@[elab_as_elim] +theorem insertMany_ind {motive : Raw α (fun _ => β) → Prop} (m : Raw α fun _ => β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (insertMany m l) := by + dsimp [insertMany] + split + · rename_i h + refine Raw₀.Const.insertMany_ind ⟨m, h⟩ l init (fun m a b h => ?_) + simpa only [Raw.insert, m.2, ↓reduceDIte] using insert m.1 a b h + · exact init + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} {k : α} : @@ -1652,6 +1692,11 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) simp only [mem_iff_contains] simp_to_raw using Raw₀.Const.contains_of_contains_insertMany_list +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} {k : α} : k ∈ m → k ∈ insertMany m l := by + simp only [mem_iff_contains] + simp_to_raw using Raw₀.Const.contains_insertMany_of_contains + theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1724,6 +1769,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF m.size ≤ (insertMany m l).size := by simp_to_raw using Raw₀.Const.size_le_size_insertMany_list ⟨m, _⟩ +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : m.size ≤ (insertMany m l).size := by + simp_to_raw using Raw₀.Const.size_le_size_insertMany ⟨m, _⟩ + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} : (insertMany m l).size ≤ m.size + l.length := by @@ -1735,6 +1784,10 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) (insertMany m l).isEmpty = (m.isEmpty && l.isEmpty) := by simp_to_raw using Raw₀.Const.isEmpty_insertMany_list +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : (insertMany m l).isEmpty → m.isEmpty := by + simp_to_raw using Raw₀.Const.isEmpty_of_isEmpty_insertMany + theorem get?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1787,6 +1840,8 @@ theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) variable {m : Raw α (fun _ => Unit)} +variable {ρ : Type w} [ForIn Id ρ α] + @[simp] theorem insertManyIfNewUnit_nil (h : m.WF) : insertManyIfNewUnit m [] = m := by @@ -1804,6 +1859,18 @@ theorem insertManyIfNewUnit_cons (h : m.WF) {l : List α} {k : α} : simp_to_raw rw [Raw₀.Const.insertManyIfNewUnit_cons] +@[elab_as_elim] +theorem insertManyIfNewUnit_ind {motive : Raw α (fun _ => Unit) → Prop} + (m : Raw α fun _ => Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l) := by + dsimp [insertManyIfNewUnit] + split + · rename_i h + refine Raw₀.Const.insertManyIfNewUnit_ind ⟨m, h⟩ l init (fun m a h => ?_) + simpa only [Raw.insertIfNew, m.2, ↓reduceDIte] using insert m.1 a h + · exact init + @[simp] theorem contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} : @@ -1822,6 +1889,11 @@ theorem mem_of_mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h simp only [mem_iff_contains] simp_to_raw using Raw₀.Const.contains_of_contains_insertManyIfNewUnit_list +theorem mem_insertManyIfNewUnit_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} {k : α} : k ∈ m → k ∈ insertManyIfNewUnit m l := by + simp only [mem_iff_contains] + simp_to_raw using Raw₀.Const.contains_insertManyIfNewUnit_of_contains + theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} : ¬ k ∈ m → l.contains k = false → @@ -1909,6 +1981,10 @@ theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertManyIfNewUnit m l).size := by simp_to_raw using Raw₀.Const.size_le_size_insertManyIfNewUnit_list ⟨m, _⟩ +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : m.size ≤ (insertManyIfNewUnit m l).size := by + simp_to_raw using Raw₀.Const.size_le_size_insertManyIfNewUnit ⟨m, _⟩ + theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} : (insertManyIfNewUnit m l).size ≤ m.size + l.length := by @@ -1920,6 +1996,10 @@ theorem isEmpty_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : (insertManyIfNewUnit m l).isEmpty = (m.isEmpty && l.isEmpty) := by simp_to_raw using Raw₀.Const.isEmpty_insertManyIfNewUnit_list +theorem isEmpty_of_isEmpty_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : (insertManyIfNewUnit m l).isEmpty → m.isEmpty := by + simp_to_raw using Raw₀.Const.isEmpty_of_isEmpty_insertManyIfNewUnit + @[simp] theorem get?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} : @@ -1948,6 +2028,8 @@ theorem getD_insertManyIfNewUnit_list end Const +end insertMany + end Raw namespace Raw @@ -3092,12 +3174,15 @@ section Raw variable {α : Type u} {β : α → Type v} {m m₁ m₂ m₃ : Std.DHashMap.Raw α β} -theorem refl (m : Std.DHashMap.Raw α β) : m ~m m := ⟨.rfl⟩ +@[refl, simp] theorem refl (m : Std.DHashMap.Raw α β) : m ~m m := ⟨.rfl⟩ theorem rfl : m ~m m := ⟨.rfl⟩ -theorem symm : m₁ ~m m₂ → m₂ ~m m₁ +@[symm] theorem symm : m₁ ~m m₂ → m₂ ~m m₁ | ⟨h⟩ => ⟨h.symm⟩ theorem trans : m₁ ~m m₂ → m₂ ~m m₃ → m₁ ~m m₃ | ⟨h₁⟩, ⟨h₂⟩ => ⟨h₁.trans h₂⟩ + +instance instTrans : Trans (α := Std.DHashMap.Raw α β) Equiv Equiv Equiv := ⟨trans⟩ + theorem comm : m₁ ~m m₂ ↔ m₂ ~m m₁ := ⟨symm, symm⟩ theorem congr_left (h : m₁ ~m m₂) : m₁ ~m m₃ ↔ m₂ ~m m₃ := ⟨h.symm.trans, h.trans⟩ theorem congr_right (h : m₁ ~m m₂) : m₃ ~m m₁ ↔ m₃ ~m m₂ := @@ -3265,11 +3350,23 @@ theorem constModify [EquivBEq α] [LawfulHashable α] (h₁ : m₁.WF) (h₂ : m Const.modify m₁ k f ~m Const.modify m₂ k f := by simp_to_raw using Raw₀.Const.modify_equiv_congr +theorem of_forall_getKey_eq_of_forall_constGet?_eq [EquivBEq α] [LawfulHashable α] + (h₁ : m₁.WF) (h₂ : m₂.WF) : (∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') → + (∀ k, Const.get? m₁ k = Const.get? m₂ k) → m₁ ~m m₂ := by + simp only [mem_iff_contains] + simp_to_raw using Raw₀.Const.equiv_of_forall_getKey_eq_of_forall_get?_eq + +set_option linter.deprecated false in +@[deprecated of_forall_getKey_eq_of_forall_constGet?_eq (since := "2025-04-25")] theorem of_forall_getKey?_eq_of_forall_constGet?_eq [EquivBEq α] [LawfulHashable α] (h₁ : m₁.WF) (h₂ : m₂.WF) : (∀ k, m₁.getKey? k = m₂.getKey? k) → (∀ k, Const.get? m₁ k = Const.get? m₂ k) → m₁ ~m m₂ := by simp_to_raw using Raw₀.Const.equiv_of_forall_getKey?_eq_of_forall_get?_eq +theorem of_forall_constGet?_eq [LawfulBEq α] + (h₁ : m₁.WF) (h₂ : m₂.WF) : (∀ k, Const.get? m₁ k = Const.get? m₂ k) → m₁ ~m m₂ := by + simp_to_raw using Raw₀.Const.equiv_of_forall_get?_eq + theorem of_forall_getKey?_unit_eq [EquivBEq α] [LawfulHashable α] {m₁ m₂ : DHashMap.Raw α fun _ => Unit} (h₁ : m₁.WF) (h₂ : m₂.WF) : (∀ k, m₁.getKey? k = m₂.getKey? k) → m₁ ~m m₂ := by diff --git a/src/Std/Data/ExtDHashMap.lean b/src/Std/Data/ExtDHashMap.lean new file mode 100644 index 0000000000..77718d42e5 --- /dev/null +++ b/src/Std/Data/ExtDHashMap.lean @@ -0,0 +1,8 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtDHashMap.Basic +import Std.Data.ExtDHashMap.Lemmas diff --git a/src/Std/Data/ExtDHashMap/Basic.lean b/src/Std/Data/ExtDHashMap/Basic.lean new file mode 100644 index 0000000000..3f72892965 --- /dev/null +++ b/src/Std/Data/ExtDHashMap/Basic.lean @@ -0,0 +1,337 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.DHashMap.Lemmas + +/-! +# Extensional dependent hash maps + +This file develops the type `Std.ExtDHashMap` of extensional dependent hash maps. + +Lemmas about the operations on `Std.ExtDHashMap` are available in the +module `Std.Data.ExtDHashMap.Lemmas`. +-/ + +set_option linter.missingDocs true +set_option autoImplicit false + +attribute [local instance] Std.DHashMap.isSetoid + +universe u v w + +variable {α : Type u} {β : α → Type v} {γ : α → Type w} + +variable {_ : BEq α} {_ : Hashable α} + +open scoped Std.DHashMap + +namespace Std + +/-- +Extensional dependent hash maps. + +This is a simple separate-chaining hash table. The data of the hash map consists of a cached size +and an array of buckets, where each bucket is a linked list of key-value pais. The number of buckets +is always a power of two. The hash map doubles its size upon inserting an element such that the +number of elements is more than 75% of the number of buckets. + +The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to +avoid expensive copies. + +The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by +the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==` +must be an equivalence relation and `a == b` must imply `hash a = hash b` (see also the +`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq +instance is lawful, i.e., if `a == b` implies `a = b`. + +In contrast to regular dependent hash maps, `Std.ExtDHashMap` offers several extensionality lemmas +and therefore has more lemmas about equality of hash maps. This however also makes it lose the +ability to iterate freely over the hash map. + +These hash maps contain a bundled well-formedness invariant, which means that they cannot +be used in nested inductive types. For these use cases, `Std.DHashMap.Raw` and +`Std.DHashMap.Raw.WF` unbundle the invariant from the hash map. When in doubt, prefer +`DHashMap` over `DHashMap.Raw`. +-/ +def ExtDHashMap (α : Type u) (β : α → Type v) [BEq α] [Hashable α] := + Quotient (DHashMap.isSetoid α β) + +namespace ExtDHashMap + +@[inline, inherit_doc DHashMap.emptyWithCapacity] +def emptyWithCapacity [BEq α] [Hashable α] + (capacity := 8) : ExtDHashMap α β := + Quotient.mk' (DHashMap.emptyWithCapacity capacity) + +instance [BEq α] [Hashable α] : EmptyCollection (ExtDHashMap α β) where + emptyCollection := emptyWithCapacity + +instance [BEq α] [Hashable α] : Inhabited (DHashMap α β) where + default := ∅ + +@[inline, inherit_doc DHashMap.insert] +def insert [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) (a : α) + (b : β a) : ExtDHashMap α β := + m.lift (fun m => Quotient.mk' (m.insert a b)) + (fun m m' (h : m ~m m') => Quotient.sound (h.insert a b)) + +instance [EquivBEq α] [LawfulHashable α] : Singleton ((a : α) × β a) (ExtDHashMap α β) where + singleton | ⟨a, b⟩ => (∅ : ExtDHashMap α β).insert a b + +instance [EquivBEq α] [LawfulHashable α] : Insert ((a : α) × β a) (ExtDHashMap α β) where + insert | ⟨a, b⟩, s => s.insert a b + +instance [EquivBEq α] [LawfulHashable α] : LawfulSingleton ((a : α) × β a) (ExtDHashMap α β) := + ⟨fun _ => rfl⟩ + +@[inline, inherit_doc DHashMap.insertIfNew] +def insertIfNew [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) + (a : α) (b : β a) : ExtDHashMap α β := + m.lift (fun m => Quotient.mk' (m.insertIfNew a b)) + (fun m m' (h : m ~m m') => Quotient.sound (h.insertIfNew a b)) + +@[inline, inherit_doc DHashMap.containsThenInsert] +def containsThenInsert [EquivBEq α] [LawfulHashable α] + (m : ExtDHashMap α β) (a : α) (b : β a) : Bool × ExtDHashMap α β := + m.lift (fun m => let m' := m.containsThenInsert a b; ⟨m'.1, Quotient.mk' m'.2⟩) + (fun m m' (h : m ~m m') => + Prod.ext + (m.containsThenInsert_fst.symm ▸ m'.containsThenInsert_fst.symm ▸ h.contains_eq) + (Quotient.sound <| + m.containsThenInsert_snd.symm ▸ m'.containsThenInsert_snd.symm ▸ h.insert a b)) + +@[inline, inherit_doc DHashMap.containsThenInsertIfNew] +def containsThenInsertIfNew [EquivBEq α] [LawfulHashable α] + (m : ExtDHashMap α β) (a : α) (b : β a) : Bool × ExtDHashMap α β := + m.lift (fun m => let m' := m.containsThenInsertIfNew a b; ⟨m'.1, Quotient.mk' m'.2⟩) + (fun m m' (h : m ~m m') => + Prod.ext + (m.containsThenInsertIfNew_fst.symm ▸ m'.containsThenInsertIfNew_fst.symm ▸ h.contains_eq) + (Quotient.sound <| + m.containsThenInsertIfNew_snd.symm ▸ m'.containsThenInsertIfNew_snd.symm ▸ h.insertIfNew a b)) + +@[inline, inherit_doc DHashMap.getThenInsertIfNew?] +def getThenInsertIfNew? [LawfulBEq α] + (m : ExtDHashMap α β) (a : α) (b : β a) : Option (β a) × ExtDHashMap α β := + m.lift (fun m => let m' := m.getThenInsertIfNew? a b; ⟨m'.1, Quotient.mk' m'.2⟩) + (fun m m' (h : m ~m m') => + Prod.ext + (m.getThenInsertIfNew?_fst.symm ▸ m'.getThenInsertIfNew?_fst.symm ▸ h.get?_eq) + (Quotient.sound <| + m.getThenInsertIfNew?_snd.symm ▸ m'.getThenInsertIfNew?_snd.symm ▸ h.insertIfNew a b)) + +@[inline, inherit_doc DHashMap.get?] +def get? [LawfulBEq α] (m : ExtDHashMap α β) + (a : α) : Option (β a) := + m.lift (fun m => m.get? a) (fun m m' (h : m ~m m') => h.get?_eq) + +@[inline, inherit_doc DHashMap.contains] +def contains [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) (a : α) : + Bool := + m.lift (fun m => m.contains a) (fun m m' (h : m ~m m') => h.contains_eq) + +instance [EquivBEq α] [LawfulHashable α] : Membership α (ExtDHashMap α β) where + mem m a := m.contains a + +instance [EquivBEq α] [LawfulHashable α] {m : ExtDHashMap α β} {a : α} : Decidable (a ∈ m) := + inferInstanceAs <| Decidable (m.contains a) + +@[inline, inherit_doc DHashMap.get] +def get [LawfulBEq α] (m : ExtDHashMap α β) (a : α) + (h : a ∈ m) : β a := + m.pliftOn (fun m h' => m.get a (h' ▸ h :)) + (fun m m' _ _ (h : m ~m m') => h.get_eq _) + +@[inline, inherit_doc DHashMap.get!] +def get! [LawfulBEq α] (m : ExtDHashMap α β) + (a : α) [Inhabited (β a)] : β a := + m.lift (fun m => m.get! a) (fun m m' (h : m ~m m') => h.get!_eq) + +@[inline, inherit_doc DHashMap.getD] +def getD [LawfulBEq α] (m : ExtDHashMap α β) + (a : α) (fallback : β a) : β a := + m.lift (fun m => m.getD a fallback) (fun m m' (h : m ~m m') => h.getD_eq) + +@[inline, inherit_doc DHashMap.erase] +def erase [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) (a : α) : + ExtDHashMap α β := + m.lift (fun m => Quotient.mk' (m.erase a)) + (fun m m' (h : m ~m m') => Quotient.sound (h.erase a)) + +namespace Const + +variable {β : Type v} + +@[inline, inherit_doc DHashMap.Const.get?] +def get? [EquivBEq α] [LawfulHashable α] + (m : ExtDHashMap α (fun _ => β)) (a : α) : Option β := + m.lift (fun m => DHashMap.Const.get? m a) + (fun m m' (h : m ~m m') => h.constGet?_eq) + +@[inline, inherit_doc DHashMap.Const.get] +def get [EquivBEq α] [LawfulHashable α] + (m : ExtDHashMap α (fun _ => β)) (a : α) (h : a ∈ m) : β := + m.pliftOn (fun m h' => DHashMap.Const.get m a (h' ▸ h :)) + (fun m m' _ _ (h : m ~m m') => h.constGet_eq _) + +@[inline, inherit_doc DHashMap.Const.getD] +def getD [EquivBEq α] [LawfulHashable α] + (m : ExtDHashMap α (fun _ => β)) (a : α) (fallback : β) : β := + m.lift (fun m => DHashMap.Const.getD m a fallback) + (fun m m' (h : m ~m m') => h.constGetD_eq) + +@[inline, inherit_doc DHashMap.Const.get!] +def get! [EquivBEq α] [LawfulHashable α] [Inhabited β] + (m : ExtDHashMap α (fun _ => β)) (a : α) : β := + m.lift (fun m => DHashMap.Const.get! m a) + (fun m m' (h : m ~m m') => h.constGet!_eq) + +@[inline, inherit_doc DHashMap.Const.getThenInsertIfNew?] +def getThenInsertIfNew? [EquivBEq α] [LawfulHashable α] + (m : ExtDHashMap α (fun _ => β)) (a : α) (b : β) : + Option β × ExtDHashMap α (fun _ => β) := + m.lift (fun m => + let m' := DHashMap.Const.getThenInsertIfNew? m a b + ⟨m'.1, Quotient.mk' m'.2⟩) + (fun m m' (h : m ~m m') => + Prod.ext + (DHashMap.Const.getThenInsertIfNew?_fst.symm ▸ + DHashMap.Const.getThenInsertIfNew?_fst.symm ▸ h.constGet?_eq) + (Quotient.sound <| + DHashMap.Const.getThenInsertIfNew?_snd.symm ▸ + DHashMap.Const.getThenInsertIfNew?_snd.symm ▸ h.insertIfNew a b)) + +end Const + +@[inline, inherit_doc DHashMap.getKey?] +def getKey? [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) (a : α) : Option α := + m.lift (fun m => m.getKey? a) (fun m m' (h : m ~m m') => h.getKey?_eq) + +@[inline, inherit_doc DHashMap.getKey] +def getKey [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) (a : α) (h : a ∈ m) : α := + m.pliftOn (fun m h' => m.getKey a (h' ▸ h :)) + (fun m m' _ _ (h : m ~m m') => h.getKey_eq _) + +@[inline, inherit_doc DHashMap.getKey!] +def getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (m : ExtDHashMap α β) (a : α) : α := + m.lift (fun m => m.getKey! a) (fun m m' (h : m ~m m') => h.getKey!_eq) + +@[inline, inherit_doc DHashMap.getKeyD] +def getKeyD [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) (a : α) (fallback : α) : α := + m.lift (fun m => m.getKeyD a fallback) + (fun m m' (h : m ~m m') => h.getKeyD_eq) + +@[inline, inherit_doc DHashMap.size] +def size [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) : Nat := + m.lift (fun m => m.size) (fun m m' (h : m ~m m') => h.size_eq) + +@[inline, inherit_doc DHashMap.isEmpty] +def isEmpty [EquivBEq α] [LawfulHashable α] (m : ExtDHashMap α β) : Bool := + m.lift (fun m => m.isEmpty) (fun m m' (h : m ~m m') => h.isEmpty_eq) + +-- TODO: add fold similar to `Finset.fold` + +@[inline, inherit_doc DHashMap.filter] +def filter [EquivBEq α] [LawfulHashable α] (f : (a : α) → β a → Bool) + (m : ExtDHashMap α β) : ExtDHashMap α β := + m.lift (fun m => Quotient.mk' (m.filter f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.filter f)) + +@[inline, inherit_doc DHashMap.map] +def map [EquivBEq α] [LawfulHashable α] (f : (a : α) → β a → γ a) + (m : ExtDHashMap α β) : ExtDHashMap α γ := + m.lift (fun m => Quotient.mk' (m.map f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.map f)) + +@[inline, inherit_doc DHashMap.filterMap] +def filterMap [EquivBEq α] [LawfulHashable α] (f : (a : α) → β a → Option (γ a)) + (m : ExtDHashMap α β) : ExtDHashMap α γ := + m.lift (fun m => Quotient.mk' (m.filterMap f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.filterMap f)) + +@[inline, inherit_doc DHashMap.modify] +def modify [LawfulBEq α] (m : ExtDHashMap α β) + (a : α) (f : β a → β a) : ExtDHashMap α β := + m.lift (fun m => Quotient.mk' (m.modify a f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.modify a f)) + +@[inline, inherit_doc DHashMap.Const.modify] +def Const.modify [EquivBEq α] [LawfulHashable α] {β : Type v} (m : ExtDHashMap α (fun _ => β)) + (a : α) (f : β → β) : ExtDHashMap α (fun _ => β) := + m.lift (fun m => Quotient.mk' (DHashMap.Const.modify m a f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.constModify a f)) + +@[inline, inherit_doc DHashMap.alter] +def alter [LawfulBEq α] (m : ExtDHashMap α β) + (a : α) (f : Option (β a) → Option (β a)) : ExtDHashMap α β := + m.lift (fun m => Quotient.mk' (m.alter a f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.alter a f)) + +@[inline, inherit_doc DHashMap.Const.alter] +def Const.alter [EquivBEq α] [LawfulHashable α] {β : Type v} (m : ExtDHashMap α (fun _ => β)) + (a : α) (f : Option β → Option β) : ExtDHashMap α (fun _ => β) := + m.lift (fun m => Quotient.mk' (DHashMap.Const.alter m a f)) + (fun m m' (h : m ~m m') => Quotient.sound (h.constAlter a f)) + +/- +Note: We can't use the existing functions because weird (noncomputable) `ForIn` instances +can break congruence. The subtype is still used to provide the `insertMany_ind` theorem. +-/ + +@[inline, inherit_doc DHashMap.insertMany] +def insertMany [EquivBEq α] [LawfulHashable α] {ρ : Type w} + [ForIn Id ρ ((a : α) × β a)] (m : ExtDHashMap α β) (l : ρ) : ExtDHashMap α β := Id.run do + let mut m : { x // ∀ P : ExtDHashMap α β → Prop, + P m → (∀ {m a b}, P m → P (m.insert a b)) → P x } := ⟨m, fun _ h _ => h⟩ + for ⟨a, b⟩ in l do + m := ⟨m.1.insert a b, fun _ init step => step (m.2 _ init step)⟩ + return m.1 + +@[inline, inherit_doc DHashMap.Const.insertMany] +def Const.insertMany [EquivBEq α] [LawfulHashable α] {β : Type v} {ρ : Type w} + [ForIn Id ρ (α × β)] (m : ExtDHashMap α (fun _ => β)) + (l : ρ) : ExtDHashMap α (fun _ => β) := Id.run do + let mut m : { x // ∀ P : ExtDHashMap α (fun _ => β) → Prop, + P m → (∀ {m a b}, P m → P (m.insert a b)) → P x } := ⟨m, fun _ h _ => h⟩ + for (a, b) in l do + m := ⟨m.1.insert a b, fun _ init step => step (m.2 _ init step)⟩ + return m.1 + +@[inline, inherit_doc DHashMap.Const.insertManyIfNewUnit] +def Const.insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] {ρ : Type w} + [ForIn Id ρ α] (m : ExtDHashMap α (fun _ => Unit)) + (l : ρ) : ExtDHashMap α (fun _ => Unit) := Id.run do + let mut m : { x // ∀ P : ExtDHashMap α (fun _ => Unit) → Prop, + P m → (∀ {m a}, P m → P (m.insertIfNew a ())) → P x } := ⟨m, fun _ h _ => h⟩ + for a in l do + m := ⟨m.1.insertIfNew a (), fun _ init step => step (m.2 _ init step)⟩ + return m.1 + +-- TODO (after verification): partition, union + +@[inline, inherit_doc DHashMap.Const.unitOfArray] +def Const.unitOfArray [BEq α] [Hashable α] (l : Array α) : + ExtDHashMap α (fun _ => Unit) := + Quotient.mk' (DHashMap.Const.unitOfArray l) + +@[inline, inherit_doc DHashMap.ofList] +def ofList [BEq α] [Hashable α] (l : List ((a : α) × β a)) : + ExtDHashMap α β := + Quotient.mk' (DHashMap.ofList l) + +@[inline, inherit_doc DHashMap.Const.ofList] +def Const.ofList {β : Type v} [BEq α] [Hashable α] (l : List (α × β)) : + ExtDHashMap α (fun _ => β) := + Quotient.mk' (DHashMap.Const.ofList l) + +@[inline, inherit_doc DHashMap.Const.unitOfList] +def Const.unitOfList [BEq α] [Hashable α] (l : List α) : + ExtDHashMap α (fun _ => Unit) := + Quotient.mk' (DHashMap.Const.unitOfList l) + +end ExtDHashMap + +end Std diff --git a/src/Std/Data/ExtDHashMap/Lemmas.lean b/src/Std/Data/ExtDHashMap/Lemmas.lean new file mode 100644 index 0000000000..0369dcad15 --- /dev/null +++ b/src/Std/Data/ExtDHashMap/Lemmas.lean @@ -0,0 +1,3289 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtDHashMap.Basic + +/-! +# Extensional dependent hash map lemmas + +This file contains lemmas about `Std.ExtDHashMap`. +-/ + +set_option linter.missingDocs true +set_option autoImplicit false + +attribute [local instance] Std.DHashMap.isSetoid + +universe u v w w' + +variable {α : Type u} {_ : BEq α} {_ : Hashable α} +variable {β : α → Type v} {γ : α → Type w} + +namespace Std.ExtDHashMap + +variable {m : ExtDHashMap α β} + +@[simp] +theorem isEmpty_iff [EquivBEq α] [LawfulHashable α] : m.isEmpty ↔ m = ∅ := by + rcases m with ⟨m⟩ + refine m.equiv_empty_iff_isEmpty.symm.trans ?_ + exact ⟨fun h => Quotient.sound h, Quotient.exact⟩ + +@[simp] +theorem isEmpty_eq_false_iff [EquivBEq α] [LawfulHashable α] : m.isEmpty = false ↔ ¬m = ∅ := + (Bool.not_eq_true _).symm.to_iff.trans (not_congr isEmpty_iff) + +@[simp] +theorem empty_eq : ∅ = m ↔ m = ∅ := eq_comm + +@[simp] +theorem emptyWithCapacity_eq [EquivBEq α] [LawfulHashable α] {c} : (emptyWithCapacity c : ExtDHashMap α β) = ∅ := + isEmpty_iff.mp DHashMap.isEmpty_emptyWithCapacity + +@[simp] +theorem not_insert_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + ¬m.insert k v = ∅ := + m.inductionOn fun _ => isEmpty_eq_false_iff.mp DHashMap.isEmpty_insert + +theorem mem_iff_contains [EquivBEq α] [LawfulHashable α] {a : α} : a ∈ m ↔ m.contains a := + Iff.rfl + +theorem contains_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : m.contains a = m.contains b := + m.inductionOn fun _ => DHashMap.contains_congr hab + +theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : a ∈ m ↔ b ∈ m := + m.inductionOn fun _ => DHashMap.mem_congr hab + +@[simp] +theorem contains_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : DHashMap α β).contains a = false := + DHashMap.contains_empty + +@[simp] +theorem not_mem_empty [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ (∅ : DHashMap α β) := + DHashMap.not_mem_empty + +theorem eq_empty_iff_forall_contains [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ ∀ a, m.contains a = false := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.isEmpty_iff_forall_contains + +theorem eq_empty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ ∀ a, ¬a ∈ m := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.isEmpty_iff_forall_not_mem + +@[simp] +theorem insert_eq_insert [EquivBEq α] [LawfulHashable α] {p : (a : α) × β a} : + Insert.insert p m = m.insert p.1 p.2 := + rfl + +@[simp] +theorem singleton_eq_insert [EquivBEq α] [LawfulHashable α] {p : (a : α) × β a} : + Singleton.singleton p = (∅ : DHashMap α β).insert p.1 p.2 := + rfl + +@[simp] +theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + (m.insert k v).contains a = (k == a || m.contains a) := + m.inductionOn fun _ => DHashMap.contains_insert + +@[simp] +theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : a ∈ m.insert k v ↔ k == a ∨ a ∈ m := + m.inductionOn fun _ => DHashMap.mem_insert + +theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + (m.insert k v).contains a → (k == a) = false → m.contains a := + m.inductionOn fun _ => DHashMap.contains_of_contains_insert + +theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : a ∈ m.insert k v → (k == a) = false → a ∈ m := + m.inductionOn fun _ => DHashMap.mem_of_mem_insert + +theorem contains_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : (m.insert k v).contains k := by simp + +theorem mem_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : k ∈ m.insert k v := by simp + +@[simp] +theorem size_empty [EquivBEq α] [LawfulHashable α] : (∅ : ExtDHashMap α β).size = 0 := rfl + +theorem eq_empty_iff_size_eq_zero [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ m.size = 0 := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => + (Bool.eq_iff_iff.mp DHashMap.isEmpty_eq_size_eq_zero).trans beq_iff_eq + +theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.insert k v).size = if k ∈ m then m.size else m.size + 1 := + m.inductionOn fun _ => DHashMap.size_insert + +theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : m.size ≤ (m.insert k v).size := + m.inductionOn fun _ => DHashMap.size_le_size_insert + +theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : (m.insert k v).size ≤ m.size + 1 := + m.inductionOn fun _ => DHashMap.size_insert_le + +@[simp] +theorem erase_empty [EquivBEq α] [LawfulHashable α] {k : α} : (∅ : ExtDHashMap α β).erase k = ∅ := + congrArg Quotient.mk' DHashMap.erase_empty + +@[simp] +theorem erase_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} : + m.erase k = ∅ ↔ m = ∅ ∨ m.size = 1 ∧ k ∈ m := by + apply isEmpty_iff.symm.trans + rcases m with ⟨m⟩ + rw [← isEmpty_iff] + dsimp only [erase, isEmpty, Quotient.mk', Quotient.mk, Quotient.lift] + simp only [DHashMap.isEmpty_erase, Bool.or_eq_true, Bool.and_eq_true, beq_iff_eq] + rfl + +@[simp] +theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).contains a = (!(k == a) && m.contains a) := + m.inductionOn fun _ => DHashMap.contains_erase + +@[simp] +theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + a ∈ m.erase k ↔ (k == a) = false ∧ a ∈ m := by + simp [mem_iff_contains, contains_erase] + +theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).contains a → m.contains a := + m.inductionOn fun _ => DHashMap.contains_of_contains_erase + +theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.erase k → a ∈ m := by + simp + +theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} : + (m.erase k).size = if k ∈ m then m.size - 1 else m.size := + m.inductionOn fun _ => DHashMap.size_erase + +theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size := + m.inductionOn fun _ => DHashMap.size_erase_le + +theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} : + m.size ≤ (m.erase k).size + 1 := + m.inductionOn fun _ => DHashMap.size_le_size_erase + +@[simp] +theorem containsThenInsert_fst [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : (m.containsThenInsert k v).1 = m.contains k := + m.inductionOn fun _ => DHashMap.containsThenInsert_fst + +@[simp] +theorem containsThenInsert_snd [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : (m.containsThenInsert k v).2 = m.insert k v := + m.inductionOn fun _ => congrArg Quotient.mk' DHashMap.containsThenInsert_snd + +@[simp] +theorem containsThenInsertIfNew_fst [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.containsThenInsertIfNew k v).1 = m.contains k := + m.inductionOn fun _ => DHashMap.containsThenInsertIfNew_fst + +@[simp] +theorem containsThenInsertIfNew_snd [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.containsThenInsertIfNew k v).2 = m.insertIfNew k v := + m.inductionOn fun _ => congrArg Quotient.mk' DHashMap.containsThenInsertIfNew_snd + +@[simp] +theorem get?_empty [LawfulBEq α] {a : α} : (∅ : ExtDHashMap α β).get? a = none := + DHashMap.get?_empty + +theorem get?_insert [LawfulBEq α] {a k : α} {v : β k} : (m.insert k v).get? a = + if h : k == a then some (cast (congrArg β (eq_of_beq h)) v) else m.get? a := + m.inductionOn fun _ => DHashMap.get?_insert + +@[simp] +theorem get?_insert_self [LawfulBEq α] {k : α} {v : β k} : (m.insert k v).get? k = some v := + m.inductionOn fun _ => DHashMap.get?_insert_self + +theorem contains_eq_isSome_get? [LawfulBEq α] {a : α} : m.contains a = (m.get? a).isSome := + m.inductionOn fun _ => DHashMap.contains_eq_isSome_get? + +theorem mem_iff_isSome_get? [LawfulBEq α] {a : α} : a ∈ m ↔ (m.get? a).isSome := + m.inductionOn fun _ => DHashMap.mem_iff_isSome_get? + +theorem get?_eq_none_of_contains_eq_false [LawfulBEq α] {a : α} : + m.contains a = false → m.get? a = none := + m.inductionOn fun _ => DHashMap.get?_eq_none_of_contains_eq_false + +theorem get?_eq_none [LawfulBEq α] {a : α} : ¬a ∈ m → m.get? a = none := by + simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false + +theorem get?_erase [LawfulBEq α] {k a : α} : + (m.erase k).get? a = if k == a then none else m.get? a := + m.inductionOn fun _ => DHashMap.get?_erase + +@[simp] +theorem get?_erase_self [LawfulBEq α] {k : α} : (m.erase k).get? k = none := + m.inductionOn fun _ => DHashMap.get?_erase_self + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +@[simp] +theorem get?_empty [EquivBEq α] [LawfulHashable α] {a : α} : get? (∅ : ExtDHashMap α (fun _ => β)) a = none := + DHashMap.Const.get?_empty + +theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + get? (m.insert k v) a = if k == a then some v else get? m a := + m.inductionOn fun _ => DHashMap.Const.get?_insert + +@[simp] +theorem get?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + get? (m.insert k v) k = some v := + m.inductionOn fun _ => DHashMap.Const.get?_insert_self + +theorem contains_eq_isSome_get? [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = (get? m a).isSome := + m.inductionOn fun _ => DHashMap.Const.contains_eq_isSome_get? + +theorem mem_iff_isSome_get? [EquivBEq α] [LawfulHashable α] {a : α} : a ∈ m ↔ (get? m a).isSome := + m.inductionOn fun _ => DHashMap.Const.mem_iff_isSome_get? + +theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = false → get? m a = none := + m.inductionOn fun _ => DHashMap.Const.get?_eq_none_of_contains_eq_false + +theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m → get? m a = none := by + simpa [mem_iff_contains] using get?_eq_none_of_contains_eq_false + +theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + Const.get? (m.erase k) a = if k == a then none else get? m a := + m.inductionOn fun _ => DHashMap.Const.get?_erase + +@[simp] +theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : get? (m.erase k) k = none := + m.inductionOn fun _ => DHashMap.Const.get?_erase_self + +theorem get?_eq_get? [LawfulBEq α] {a : α} : get? m a = m.get? a := + m.inductionOn fun _ => DHashMap.Const.get?_eq_get? + +theorem get?_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : get? m a = get? m b := + m.inductionOn fun _ => DHashMap.Const.get?_congr hab + +end Const + +theorem get_insert [LawfulBEq α] {k a : α} {v : β k} {h₁} : + (m.insert k v).get a h₁ = + if h₂ : k == a then + cast (congrArg β (eq_of_beq h₂)) v + else + m.get a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) := + m.inductionOn (fun _ _ => DHashMap.get_insert) h₁ + +@[simp] +theorem get_insert_self [LawfulBEq α] {k : α} {v : β k} : + (m.insert k v).get k mem_insert_self = v := + m.inductionOn fun _ => DHashMap.get_insert_self + +@[simp] +theorem get_erase [LawfulBEq α] {k a : α} {h'} : + (m.erase k).get a h' = m.get a (mem_of_mem_erase h') := + m.inductionOn (fun _ _ => DHashMap.get_erase) h' + +theorem get?_eq_some_get [LawfulBEq α] {a : α} (h) : m.get? a = some (m.get a h) := + m.inductionOn (fun _ h => DHashMap.get?_eq_some_get h) h + +theorem get_eq_get_get? [LawfulBEq α] {a : α} {h} : + m.get a h = (m.get? a).get (mem_iff_isSome_get?.mp h) := + m.inductionOn (fun _ _ => DHashMap.get_eq_get_get?) h + +theorem get_get? [LawfulBEq α] {a : α} {h} : + (m.get? a).get h = m.get a (mem_iff_isSome_get?.mpr h) := + m.inductionOn (fun _ _ => DHashMap.get_get?) h + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} : + get (m.insert k v) a h₁ = + if h₂ : k == a then v else get m a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) := + m.inductionOn (fun _ _ => DHashMap.Const.get_insert) h₁ + +@[simp] +theorem get_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + get (m.insert k v) k mem_insert_self = v := + m.inductionOn fun _ => DHashMap.Const.get_insert_self + +@[simp] +theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} : + get (m.erase k) a h' = get m a (mem_of_mem_erase h') := + m.inductionOn (fun _ _ => DHashMap.Const.get_erase) h' + +theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} (h) : + get? m a = some (get m a h) := + m.inductionOn (fun _ h => DHashMap.Const.get?_eq_some_get h) h + +theorem get_eq_get_get? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + get m a h = (get? m a).get (mem_iff_isSome_get?.mp h) := + m.inductionOn (fun _ _ => DHashMap.Const.get_eq_get_get?) h + +theorem get_get? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + (get? m a).get h = get m a (mem_iff_isSome_get?.mpr h) := + m.inductionOn (fun _ _ => DHashMap.Const.get_get?) h + +theorem get_eq_get [LawfulBEq α] {a : α} {h} : get m a h = m.get a h := + m.inductionOn (fun _ _ => DHashMap.Const.get_eq_get) h + +theorem get_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) {h'} : + get m a h' = get m b ((mem_congr hab).1 h') := + m.inductionOn (fun _ hab _ => DHashMap.Const.get_congr hab) hab h' + +end Const + +@[simp] +theorem get!_empty [LawfulBEq α] {a : α} [Inhabited (β a)] : + (∅ : ExtDHashMap α β).get! a = default := + DHashMap.get!_empty + +theorem get!_insert [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} : + (m.insert k v).get! a = + if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.get! a := + m.inductionOn fun _ => DHashMap.get!_insert + +@[simp] +theorem get!_insert_self [LawfulBEq α] {a : α} [Inhabited (β a)] {b : β a} : + (m.insert a b).get! a = b := + m.inductionOn fun _ => DHashMap.get!_insert_self + +theorem get!_eq_default_of_contains_eq_false [LawfulBEq α] {a : α} [Inhabited (β a)] : + m.contains a = false → m.get! a = default := + m.inductionOn fun _ => DHashMap.get!_eq_default_of_contains_eq_false + +theorem get!_eq_default [LawfulBEq α] {a : α} [Inhabited (β a)] : + ¬a ∈ m → m.get! a = default := + m.inductionOn fun _ => DHashMap.get!_eq_default + +theorem get!_erase [LawfulBEq α] {k a : α} [Inhabited (β a)] : + (m.erase k).get! a = if k == a then default else m.get! a := + m.inductionOn fun _ => DHashMap.get!_erase + +@[simp] +theorem get!_erase_self [LawfulBEq α] {k : α} [Inhabited (β k)] : + (m.erase k).get! k = default := + m.inductionOn fun _ => DHashMap.get!_erase_self + +theorem get?_eq_some_get!_of_contains [LawfulBEq α] {a : α} [Inhabited (β a)] : + m.contains a = true → m.get? a = some (m.get! a) := + m.inductionOn fun _ => DHashMap.get?_eq_some_get!_of_contains + +theorem get?_eq_some_get! [LawfulBEq α] {a : α} [Inhabited (β a)] : + a ∈ m → m.get? a = some (m.get! a) := + m.inductionOn fun _ => DHashMap.get?_eq_some_get! + +theorem get!_eq_get!_get? [LawfulBEq α] {a : α} [Inhabited (β a)] : + m.get! a = (m.get? a).get! := + m.inductionOn fun _ => DHashMap.get!_eq_get!_get? + +theorem get_eq_get! [LawfulBEq α] {a : α} [Inhabited (β a)] {h} : + m.get a h = m.get! a := + m.inductionOn (fun _ _ => DHashMap.get_eq_get!) h + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +@[simp] +theorem get!_empty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : get! (∅ : ExtDHashMap α (fun _ => β)) a = default := + DHashMap.Const.get!_empty + +theorem get!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} : + get! (m.insert k v) a = if k == a then v else get! m a := + m.inductionOn fun _ => DHashMap.Const.get!_insert + +@[simp] +theorem get!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} {v : β} : + get! (m.insert k v) k = v := + m.inductionOn fun _ => DHashMap.Const.get!_insert_self + +theorem get!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + m.contains a = false → get! m a = default := + m.inductionOn fun _ => DHashMap.Const.get!_eq_default_of_contains_eq_false + +theorem get!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + ¬a ∈ m → get! m a = default := + m.inductionOn fun _ => DHashMap.Const.get!_eq_default + +theorem get!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} : + get! (m.erase k) a = if k == a then default else get! m a := + m.inductionOn fun _ => DHashMap.Const.get!_erase + +@[simp] +theorem get!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} : + get! (m.erase k) k = default := + m.inductionOn fun _ => DHashMap.Const.get!_erase_self + +theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + m.contains a = true → get? m a = some (get! m a) := + m.inductionOn fun _ => DHashMap.Const.get?_eq_some_get!_of_contains + +theorem get?_eq_some_get! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + a ∈ m → get? m a = some (get! m a) := + m.inductionOn fun _ => DHashMap.Const.get?_eq_some_get! + +theorem get!_eq_get!_get? [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + get! m a = (get? m a).get! := + m.inductionOn fun _ => DHashMap.Const.get!_eq_get!_get? + +theorem get_eq_get! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} {h} : + get m a h = get! m a := + m.inductionOn (fun _ _ => DHashMap.Const.get_eq_get!) h + +theorem get!_eq_get! [LawfulBEq α] [Inhabited β] {a : α} : + get! m a = m.get! a := + m.inductionOn fun _ => DHashMap.Const.get!_eq_get! + +theorem get!_congr [EquivBEq α] [LawfulHashable α] [Inhabited β] {a b : α} (hab : a == b) : + get! m a = get! m b := + m.inductionOn (fun _ hab => DHashMap.Const.get!_congr hab) hab + +end Const + +@[simp] +theorem getD_empty [LawfulBEq α] {a : α} {fallback : β a} : + (∅ : ExtDHashMap α β).getD a fallback = fallback := + DHashMap.getD_empty + +theorem getD_insert [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} : + (m.insert k v).getD a fallback = + if h : k == a then cast (congrArg β (eq_of_beq h)) v else m.getD a fallback := + m.inductionOn fun _ => DHashMap.getD_insert + +@[simp] +theorem getD_insert_self [LawfulBEq α] {k : α} {fallback v : β k} : + (m.insert k v).getD k fallback = v := + m.inductionOn fun _ => DHashMap.getD_insert_self + +theorem getD_eq_fallback_of_contains_eq_false [LawfulBEq α] {a : α} {fallback : β a} : + m.contains a = false → m.getD a fallback = fallback := + m.inductionOn fun _ => DHashMap.getD_eq_fallback_of_contains_eq_false + +theorem getD_eq_fallback [LawfulBEq α] {a : α} {fallback : β a} : + ¬a ∈ m → m.getD a fallback = fallback := + m.inductionOn fun _ => DHashMap.getD_eq_fallback + +theorem getD_erase [LawfulBEq α] {k a : α} {fallback : β a} : + (m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback := + m.inductionOn fun _ => DHashMap.getD_erase + +@[simp] +theorem getD_erase_self [LawfulBEq α] {k : α} {fallback : β k} : + (m.erase k).getD k fallback = fallback := + m.inductionOn fun _ => DHashMap.getD_erase_self + +theorem get?_eq_some_getD_of_contains [LawfulBEq α] {a : α} {fallback : β a} : + m.contains a = true → m.get? a = some (m.getD a fallback) := + m.inductionOn fun _ => DHashMap.get?_eq_some_getD_of_contains + +theorem get?_eq_some_getD [LawfulBEq α] {a : α} {fallback : β a} : + a ∈ m → m.get? a = some (m.getD a fallback) := + m.inductionOn fun _ => DHashMap.get?_eq_some_getD + +theorem getD_eq_getD_get? [LawfulBEq α] {a : α} {fallback : β a} : + m.getD a fallback = (m.get? a).getD fallback := + m.inductionOn fun _ => DHashMap.getD_eq_getD_get? + +theorem get_eq_getD [LawfulBEq α] {a : α} {fallback : β a} {h} : + m.get a h = m.getD a fallback := + m.inductionOn (fun _ _ => DHashMap.get_eq_getD) h + +theorem get!_eq_getD_default [LawfulBEq α] {a : α} [Inhabited (β a)] : + m.get! a = m.getD a default := + m.inductionOn fun _ => DHashMap.get!_eq_getD_default + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +@[simp] +theorem getD_empty [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + getD (∅ : ExtDHashMap α (fun _ => β)) a fallback = fallback := + DHashMap.Const.getD_empty + +theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} : + getD (m.insert k v) a fallback = if k == a then v else getD m a fallback := + m.inductionOn fun _ => DHashMap.Const.getD_insert + +@[simp] +theorem getD_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback v : β} : + getD (m.insert k v) k fallback = v := + m.inductionOn fun _ => DHashMap.Const.getD_insert_self + +theorem getD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} + {fallback : β} : m.contains a = false → getD m a fallback = fallback := + m.inductionOn fun _ => DHashMap.Const.getD_eq_fallback_of_contains_eq_false + +theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + ¬a ∈ m → getD m a fallback = fallback := + m.inductionOn fun _ => DHashMap.Const.getD_eq_fallback + +theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} : + getD (m.erase k) a fallback = if k == a then fallback else getD m a fallback := + m.inductionOn fun _ => DHashMap.Const.getD_erase + +@[simp] +theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} : + getD (m.erase k) k fallback = fallback := + m.inductionOn fun _ => DHashMap.Const.getD_erase_self + +theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + m.contains a = true → get? m a = some (getD m a fallback) := + m.inductionOn fun _ => DHashMap.Const.get?_eq_some_getD_of_contains + +theorem get?_eq_some_getD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + a ∈ m → get? m a = some (getD m a fallback) := + m.inductionOn fun _ => DHashMap.Const.get?_eq_some_getD + +theorem getD_eq_getD_get? [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + getD m a fallback = (get? m a).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_eq_getD_get? + +theorem get_eq_getD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} {h} : + get m a h = getD m a fallback := + m.inductionOn (fun _ _ => DHashMap.Const.get_eq_getD) h + +theorem get!_eq_getD_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + get! m a = getD m a default := + m.inductionOn fun _ => DHashMap.Const.get!_eq_getD_default + +theorem getD_eq_getD [LawfulBEq α] {a : α} {fallback : β} : + getD m a fallback = m.getD a fallback := + m.inductionOn fun _ => DHashMap.Const.getD_eq_getD + +theorem getD_congr [EquivBEq α] [LawfulHashable α] {a b : α} {fallback : β} (hab : a == b) : + getD m a fallback = getD m b fallback := + m.inductionOn (fun _ hab => DHashMap.Const.getD_congr hab) hab + +end Const + +@[simp] +theorem getKey?_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : ExtDHashMap α β).getKey? a = none := + DHashMap.getKey?_empty + +theorem getKey?_insert [EquivBEq α] [LawfulHashable α] {a k : α} {v : β k} : + (m.insert k v).getKey? a = if k == a then some k else m.getKey? a := + m.inductionOn fun _ => DHashMap.getKey?_insert + +@[simp] +theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.insert k v).getKey? k = some k := + m.inductionOn fun _ => DHashMap.getKey?_insert_self + +theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = (m.getKey? a).isSome := + m.inductionOn fun _ => DHashMap.contains_eq_isSome_getKey? + +theorem mem_iff_isSome_getKey? [EquivBEq α] [LawfulHashable α] {a : α} : + a ∈ m ↔ (m.getKey? a).isSome := + m.inductionOn fun _ => DHashMap.mem_iff_isSome_getKey? + +theorem mem_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] {k k' : α} + (h : m.getKey? k = some k') : k' ∈ m := + m.inductionOn (fun _ h => DHashMap.mem_of_getKey?_eq_some h) h + +theorem getKey?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = false → m.getKey? a = none := + m.inductionOn fun _ => DHashMap.getKey?_eq_none_of_contains_eq_false + +theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m → m.getKey? a = none := + m.inductionOn fun _ => DHashMap.getKey?_eq_none + +theorem getKey?_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).getKey? a = if k == a then none else m.getKey? a := + m.inductionOn fun _ => DHashMap.getKey?_erase + +@[simp] +theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).getKey? k = none := + m.inductionOn fun _ => DHashMap.getKey?_erase_self + +theorem getKey?_beq [EquivBEq α] [LawfulHashable α] {k : α} : + (m.getKey? k).all (· == k) := + m.inductionOn fun _ => DHashMap.getKey?_beq + +theorem getKey?_congr [EquivBEq α] [LawfulHashable α] {k k' : α} (h : k == k') : + m.getKey? k = m.getKey? k' := + m.inductionOn (fun _ h => DHashMap.getKey?_congr h) h + +theorem getKey?_eq_some_of_contains [LawfulBEq α] {k : α} (h : m.contains k) : + m.getKey? k = some k := + m.inductionOn (fun _ h => DHashMap.getKey?_eq_some_of_contains h) h + +theorem getKey?_eq_some [LawfulBEq α] {k : α} (h : k ∈ m) : m.getKey? k = some k := + m.inductionOn (fun _ h => DHashMap.getKey?_eq_some h) h + +theorem getKey_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} {h₁} : + (m.insert k v).getKey a h₁ = + if h₂ : k == a then + k + else + m.getKey a (mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) := + m.inductionOn (fun _ _ => DHashMap.getKey_insert) h₁ + +@[simp] +theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.insert k v).getKey k mem_insert_self = k := + m.inductionOn fun _ => DHashMap.getKey_insert_self + +@[simp] +theorem getKey_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} : + (m.erase k).getKey a h' = m.getKey a (mem_of_mem_erase h') := + m.inductionOn (fun _ _ => DHashMap.getKey_erase) h' + +theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] {a : α} (h) : + m.getKey? a = some (m.getKey a h) := + m.inductionOn (fun _ h => DHashMap.getKey?_eq_some_getKey h) h + +theorem getKey_eq_get_getKey? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + m.getKey a h = (m.getKey? a).get (mem_iff_isSome_getKey?.mp h) := + m.inductionOn (fun _ _ => DHashMap.getKey_eq_get_getKey?) h + +theorem get_getKey? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + (m.getKey? a).get h = m.getKey a (mem_iff_isSome_getKey?.mpr h) := + m.inductionOn (fun _ _ => DHashMap.get_getKey?) h + +theorem getKey_beq [EquivBEq α] [LawfulHashable α] {k : α} (h : k ∈ m) : m.getKey k h == k := + m.inductionOn (fun _ h => DHashMap.getKey_beq h) h + +theorem getKey_congr [EquivBEq α] [LawfulHashable α] {k₁ k₂ : α} (h : k₁ == k₂) + (h₁ : k₁ ∈ m) : m.getKey k₁ h₁ = m.getKey k₂ ((mem_congr h).mp h₁) := + m.inductionOn (fun _ h h₁ => DHashMap.getKey_congr h h₁) h h₁ + +theorem getKey_eq [LawfulBEq α] {k : α} (h : k ∈ m) : m.getKey k h = k := + m.inductionOn (fun _ h => DHashMap.getKey_eq h) h + +@[simp] +theorem getKey!_empty [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + (∅ : ExtDHashMap α β).getKey! a = default := + DHashMap.getKey!_empty + +theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β k} : + (m.insert k v).getKey! a = + if k == a then k else m.getKey! a := + m.inductionOn fun _ => DHashMap.getKey!_insert + +@[simp] +theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {b : β a} : + (m.insert a b).getKey! a = a := + m.inductionOn fun _ => DHashMap.getKey!_insert_self + +theorem getKey!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α] + {a : α} : + m.contains a = false → m.getKey! a = default := + m.inductionOn fun _ => DHashMap.getKey!_eq_default_of_contains_eq_false + +theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + ¬a ∈ m → m.getKey! a = default := + m.inductionOn fun _ => DHashMap.getKey!_eq_default + +theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} : + (m.erase k).getKey! a = if k == a then default else m.getKey! a := + m.inductionOn fun _ => DHashMap.getKey!_erase + +@[simp] +theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} : + (m.erase k).getKey! k = default := + m.inductionOn fun _ => DHashMap.getKey!_erase_self + +theorem getKey?_eq_some_getKey!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.contains a = true → m.getKey? a = some (m.getKey! a) := + m.inductionOn fun _ => DHashMap.getKey?_eq_some_getKey!_of_contains + +theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + a ∈ m → m.getKey? a = some (m.getKey! a) := + m.inductionOn fun _ => DHashMap.getKey?_eq_some_getKey! + +theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.getKey! a = (m.getKey? a).get! := + m.inductionOn fun _ => DHashMap.getKey!_eq_get!_getKey? + +theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {h} : + m.getKey a h = m.getKey! a := + m.inductionOn (fun _ _ => DHashMap.getKey_eq_getKey!) h + +theorem getKey!_congr [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} (h : k == k') : + m.getKey! k = m.getKey! k' := + m.inductionOn (fun _ h => DHashMap.getKey!_congr h) h + +theorem getKey!_eq_of_contains [LawfulBEq α] [Inhabited α] {k : α} (h : m.contains k) : + m.getKey! k = k := + m.inductionOn (fun _ h => DHashMap.getKey!_eq_of_contains h) h + +theorem getKey!_eq_of_mem [LawfulBEq α] [Inhabited α] {k : α} (h : k ∈ m) : m.getKey! k = k := + m.inductionOn (fun _ h => DHashMap.getKey!_eq_of_mem h) h + +@[simp] +theorem getKeyD_empty [EquivBEq α] [LawfulHashable α] {a fallback : α} : + (∅ : ExtDHashMap α β).getKeyD a fallback = fallback := + DHashMap.getKeyD_empty + +theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β k} : + (m.insert k v).getKeyD a fallback = + if k == a then k else m.getKeyD a fallback := + m.inductionOn fun _ => DHashMap.getKeyD_insert + +@[simp] +theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] {k fallback : α} {v : β k} : + (m.insert k v).getKeyD k fallback = k := + m.inductionOn fun _ => DHashMap.getKeyD_insert_self + +theorem getKeyD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} + {fallback : α} : + m.contains a = false → m.getKeyD a fallback = fallback := + m.inductionOn fun _ => DHashMap.getKeyD_eq_fallback_of_contains_eq_false + +theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] {a fallback : α} : + ¬a ∈ m → m.getKeyD a fallback = fallback := + m.inductionOn fun _ => DHashMap.getKeyD_eq_fallback + +theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] {k a fallback : α} : + (m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback := + m.inductionOn fun _ => DHashMap.getKeyD_erase + +@[simp] +theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] {k fallback : α} : + (m.erase k).getKeyD k fallback = fallback := + m.inductionOn fun _ => DHashMap.getKeyD_erase_self + +theorem getKey?_eq_some_getKeyD_of_contains [EquivBEq α] [LawfulHashable α] {a fallback : α} : + m.contains a = true → m.getKey? a = some (m.getKeyD a fallback) := + m.inductionOn fun _ => DHashMap.getKey?_eq_some_getKeyD_of_contains + +theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] {a fallback : α} : + a ∈ m → m.getKey? a = some (m.getKeyD a fallback) := + m.inductionOn fun _ => DHashMap.getKey?_eq_some_getKeyD + +theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] {a fallback : α} : + m.getKeyD a fallback = (m.getKey? a).getD fallback := + m.inductionOn fun _ => DHashMap.getKeyD_eq_getD_getKey? + +theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] {a fallback : α} {h} : + m.getKey a h = m.getKeyD a fallback := + m.inductionOn (fun _ _ => DHashMap.getKey_eq_getKeyD) h + +theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.getKey! a = m.getKeyD a default := + m.inductionOn fun _ => DHashMap.getKey!_eq_getKeyD_default + +theorem getKeyD_congr [EquivBEq α] [LawfulHashable α] {k k' fallback : α} + (h : k == k') : m.getKeyD k fallback = m.getKeyD k' fallback := + m.inductionOn (fun _ h => DHashMap.getKeyD_congr h) h + +theorem getKeyD_eq_of_contains [LawfulBEq α] {k fallback : α} (h : m.contains k) : + m.getKeyD k fallback = k := + m.inductionOn (fun _ h => DHashMap.getKeyD_eq_of_contains h) h + +theorem getKeyD_eq_of_mem [LawfulBEq α] {k fallback : α} (h : k ∈ m) : + m.getKeyD k fallback = k := + m.inductionOn (fun _ h => DHashMap.getKeyD_eq_of_mem h) h + +@[simp] +theorem not_insertIfNew_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + ¬m.insertIfNew k v = ∅ := + isEmpty_eq_false_iff.mp <| m.inductionOn fun _ => DHashMap.isEmpty_insertIfNew + +@[simp] +theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + (m.insertIfNew k v).contains a = (k == a || m.contains a) := + m.inductionOn fun _ => DHashMap.contains_insertIfNew + +@[simp] +theorem mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + a ∈ m.insertIfNew k v ↔ k == a ∨ a ∈ m := + m.inductionOn fun _ => DHashMap.mem_insertIfNew + +theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.insertIfNew k v).contains k := + m.inductionOn fun _ => DHashMap.contains_insertIfNew_self + +theorem mem_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + k ∈ m.insertIfNew k v := + m.inductionOn fun _ => DHashMap.mem_insertIfNew_self + +theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + (m.insertIfNew k v).contains a → (k == a) = false → m.contains a := + m.inductionOn fun _ => DHashMap.contains_of_contains_insertIfNew + +theorem mem_of_mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + a ∈ m.insertIfNew k v → (k == a) = false → a ∈ m := + m.inductionOn fun _ => DHashMap.mem_of_mem_insertIfNew + +/-- This is a restatement of `contains_of_contains_insertIfNew` that is written to exactly match the proof +obligation in the statement of `get_insertIfNew`. -/ +theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + (m.insertIfNew k v).contains a → ¬((k == a) ∧ m.contains k = false) → m.contains a := + m.inductionOn fun _ => DHashMap.contains_of_contains_insertIfNew' + +/-- This is a restatement of `mem_of_mem_insertIfNew` that is written to exactly match the proof obligation +in the statement of `get_insertIfNew`. -/ +theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + a ∈ m.insertIfNew k v → ¬((k == a) ∧ ¬k ∈ m) → a ∈ m := + m.inductionOn fun _ => DHashMap.mem_of_mem_insertIfNew' + +theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.insertIfNew k v).size = if k ∈ m then m.size else m.size + 1 := + m.inductionOn fun _ => DHashMap.size_insertIfNew + +theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + m.size ≤ (m.insertIfNew k v).size := + m.inductionOn fun _ => DHashMap.size_le_size_insertIfNew + +theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + (m.insertIfNew k v).size ≤ m.size + 1 := + m.inductionOn fun _ => DHashMap.size_insertIfNew_le + +theorem get?_insertIfNew [LawfulBEq α] {k a : α} {v : β k} : (m.insertIfNew k v).get? a = + if h : k == a ∧ ¬k ∈ m then some (cast (congrArg β (eq_of_beq h.1)) v) else m.get? a := + m.inductionOn fun _ => DHashMap.get?_insertIfNew + +theorem get_insertIfNew [LawfulBEq α] {k a : α} {v : β k} {h₁} : (m.insertIfNew k v).get a h₁ = + if h₂ : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h₂.1)) v else m.get a + (mem_of_mem_insertIfNew' h₁ h₂) := + m.inductionOn (fun _ _ => DHashMap.get_insertIfNew) h₁ + +theorem get!_insertIfNew [LawfulBEq α] {k a : α} [Inhabited (β a)] {v : β k} : + (m.insertIfNew k v).get! a = + if h : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1)) v else m.get! a := + m.inductionOn fun _ => DHashMap.get!_insertIfNew + +theorem getD_insertIfNew [LawfulBEq α] {k a : α} {fallback : β a} {v : β k} : + (m.insertIfNew k v).getD a fallback = + if h : k == a ∧ ¬k ∈ m then cast (congrArg β (eq_of_beq h.1)) v + else m.getD a fallback := + m.inductionOn fun _ => DHashMap.getD_insertIfNew + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +theorem get?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + get? (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then some v else get? m a := + m.inductionOn fun _ => DHashMap.Const.get?_insertIfNew + +theorem get_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} : + get (m.insertIfNew k v) a h₁ = + if h₂ : k == a ∧ ¬k ∈ m then v else get m a (mem_of_mem_insertIfNew' h₁ h₂) := + m.inductionOn (fun _ _ => DHashMap.Const.get_insertIfNew) h₁ + +theorem get!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} : + get! (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then v else get! m a := + m.inductionOn fun _ => DHashMap.Const.get!_insertIfNew + +theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} : + getD (m.insertIfNew k v) a fallback = + if k == a ∧ ¬k ∈ m then v else getD m a fallback := + m.inductionOn fun _ => DHashMap.Const.getD_insertIfNew + +end Const + +theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} : + getKey? (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then some k else getKey? m a := + m.inductionOn fun _ => DHashMap.getKey?_insertIfNew + +theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β k} {h₁} : + getKey (m.insertIfNew k v) a h₁ = + if h₂ : k == a ∧ ¬k ∈ m then k else getKey m a (mem_of_mem_insertIfNew' h₁ h₂) := + m.inductionOn (fun _ _ => DHashMap.getKey_insertIfNew) h₁ + +theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β k} : + getKey! (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then k else getKey! m a := + m.inductionOn fun _ => DHashMap.getKey!_insertIfNew + +theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β k} : + getKeyD (m.insertIfNew k v) a fallback = + if k == a ∧ ¬k ∈ m then k else getKeyD m a fallback := + m.inductionOn fun _ => DHashMap.getKeyD_insertIfNew + +@[simp] +theorem getThenInsertIfNew?_fst [LawfulBEq α] {k : α} {v : β k} : + (m.getThenInsertIfNew? k v).1 = m.get? k := + m.inductionOn fun _ => DHashMap.getThenInsertIfNew?_fst + +@[simp] +theorem getThenInsertIfNew?_snd [LawfulBEq α] {k : α} {v : β k} : + (m.getThenInsertIfNew? k v).2 = m.insertIfNew k v := + m.inductionOn fun _ => congrArg Quotient.mk' DHashMap.getThenInsertIfNew?_snd + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +@[simp] +theorem getThenInsertIfNew?_fst [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : (getThenInsertIfNew? m k v).1 = get? m k := + m.inductionOn fun _ => DHashMap.Const.getThenInsertIfNew?_fst + +@[simp] +theorem getThenInsertIfNew?_snd [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (getThenInsertIfNew? m k v).2 = m.insertIfNew k v := + m.inductionOn fun _ => congrArg Quotient.mk' DHashMap.Const.getThenInsertIfNew?_snd + +end Const + +section insertMany + +variable {ρ : Type w} [ForIn Id ρ ((a : α) × β a)] + +@[simp] +theorem insertMany_nil [EquivBEq α] [LawfulHashable α] : m.insertMany [] = m := rfl + +@[simp] +theorem insertMany_list_singleton [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + m.insertMany [⟨k, v⟩] = m.insert k v := rfl + +theorem insertMany_cons [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {p : (a : α) × β a} : + m.insertMany (p :: l) = (m.insert p.1 p.2).insertMany l := by + rcases p with ⟨k, v⟩ + unfold insertMany + simp only [Id.pure_eq, Id.bind_eq, Id.run, List.forIn_yield_eq_foldl, List.foldl_cons] + refine Eq.trans ?_ (Eq.symm ?_ : l.foldl (fun b a => b.insert a.1 a.2) (m.insert k v) = _) + exact (List.foldl_hom (f := Subtype.val) fun x y => rfl).symm + exact (List.foldl_hom (f := Subtype.val) fun x y => rfl).symm + +private theorem insertMany_list_mk [EquivBEq α] [LawfulHashable α] + {m : DHashMap α β} {l : List ((a : α) × β a)} : + (ExtDHashMap.insertMany (Quotient.mk _ m) l : ExtDHashMap α β) = Quotient.mk _ (m.insertMany l) := by + simp only [Quotient.mk] + induction l generalizing m with + | nil => rfl + | cons x l ih => + rcases x with ⟨k, v⟩ + simp only [insertMany_cons, DHashMap.insertMany_cons, insert, + Quotient.mk', Quotient.mk, Quotient.lift, ih] + +@[elab_as_elim] +theorem insertMany_ind [EquivBEq α] [LawfulHashable α] {motive : ExtDHashMap α β → Prop} (m : ExtDHashMap α β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l) := by + change motive (Subtype.val ?my_mvar) + exact Subtype.property ?my_mvar motive init (insert _ _ _) + +@[simp] +theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} : + (m.insertMany l).contains k = (m.contains k || (l.map Sigma.fst).contains k) := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.contains_insertMany_list + +@[simp] +theorem mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} : + k ∈ m.insertMany l ↔ k ∈ m ∨ (l.map Sigma.fst).contains k := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.mem_insertMany_list + +theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} (mem : k ∈ m.insertMany l) + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + k ∈ m := by + refine m.inductionOn (fun _ mem contains_eq_false => ?_) mem contains_eq_false + simp only [insertMany_list_mk] at mem + exact DHashMap.mem_of_mem_insertMany_list mem contains_eq_false + +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] {l : ρ} {k : α} (h' : k ∈ m) : k ∈ m.insertMany l := + insertMany_ind m l h' fun _ _ _ h => mem_insert.mpr (.inr h) + +theorem get?_insertMany_list_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (m.insertMany l).get? k = m.get? k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.get?_insertMany_list_of_contains_eq_false contains_eq_false + +theorem get?_insertMany_list_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (m.insertMany l).get? k' = some (cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v) := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.get?_insertMany_list_of_mem k_beq distinct mem + +theorem get_insertMany_list_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) + {h} : + (m.insertMany l).get k h = + m.get k (mem_of_mem_insertMany_list h contains_eq_false) := by + refine m.inductionOn (fun _ contains_eq_false _ => ?_) contains_eq_false h + simp only [insertMany_list_mk] + exact DHashMap.get_insertMany_list_of_contains_eq_false contains_eq_false + +theorem get_insertMany_list_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) + {h} : + (m.insertMany l).get k' h = cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v := by + refine m.inductionOn (fun _ k_beq distinct mem _ => ?_) k_beq distinct mem h + simp only [insertMany_list_mk] + exact DHashMap.get_insertMany_list_of_mem k_beq distinct mem + +theorem get!_insertMany_list_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} [Inhabited (β k)] + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (m.insertMany l).get! k = m.get! k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.get!_insertMany_list_of_contains_eq_false contains_eq_false + +theorem get!_insertMany_list_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} [Inhabited (β k')] + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (m.insertMany l).get! k' = cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.get!_insertMany_list_of_mem k_beq distinct mem + +theorem getD_insertMany_list_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} {fallback : β k} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (m.insertMany l).getD k fallback = m.getD k fallback := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.getD_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getD_insertMany_list_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} {fallback : β k'} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (m.insertMany l).getD k' fallback = cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.getD_insertMany_list_of_mem k_beq distinct mem + +theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (m.insertMany l).getKey? k = m.getKey? k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.getKey?_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey?_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) : + (m.insertMany l).getKey? k' = some k := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.getKey?_insertMany_list_of_mem k_beq distinct mem + +theorem getKey_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) + {h} : + (m.insertMany l).getKey k h = + m.getKey k (mem_of_mem_insertMany_list h contains_eq_false) := by + refine m.inductionOn (fun _ contains_eq_false _ => ?_) contains_eq_false h + simp only [insertMany_list_mk] + exact DHashMap.getKey_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) + {h} : + (m.insertMany l).getKey k' h = k := by + refine m.inductionOn (fun _ k_beq distinct mem _ => ?_) k_beq distinct mem h + simp only [insertMany_list_mk] + exact DHashMap.getKey_insertMany_list_of_mem k_beq distinct mem + +theorem getKey!_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (m.insertMany l).getKey! k = m.getKey! k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.getKey!_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey!_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List ((a : α) × β a)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) : + (m.insertMany l).getKey! k' = k := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.getKey!_insertMany_list_of_mem k_beq distinct mem + +theorem getKeyD_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k fallback : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (m.insertMany l).getKeyD k fallback = m.getKeyD k fallback := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.getKeyD_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKeyD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} + {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) : + (m.insertMany l).getKeyD k' fallback = k := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.getKeyD_insertMany_list_of_mem k_beq distinct mem + +theorem size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (∀ (a : α), a ∈ m → (l.map Sigma.fst).contains a = false) → + (m.insertMany l).size = m.size + l.length := by + refine m.inductionOn (fun _ distinct => ?_) distinct + simp only [insertMany_list_mk] + exact DHashMap.size_insertMany_list distinct + +theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} : + m.size ≤ (m.insertMany l).size := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.size_le_size_insertMany_list + +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] {l : ρ} : m.size ≤ (m.insertMany l).size := + insertMany_ind m l (Nat.le_refl _) fun _ _ _ h => Nat.le_trans h size_le_size_insert + +theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} : + (m.insertMany l).size ≤ m.size + l.length := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.size_insertMany_list_le + +@[simp] +theorem insertMany_list_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List ((a : α) × β a)} : + m.insertMany l = ∅ ↔ m = ∅ ∧ l = [] := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk, ← isEmpty_iff, ← List.isEmpty_iff, + Bool.coe_iff_coe, ← Bool.and_eq_true] + exact DHashMap.isEmpty_insertMany_list + +theorem eq_empty_of_insertMany_eq_empty [EquivBEq α] [LawfulHashable α] {l : ρ} : + m.insertMany l = ∅ → m = ∅ := + insertMany_ind m l id fun _ _ _ _ h => absurd h not_insert_eq_empty + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} +variable {ρ : Type w} [ForIn Id ρ (α × β)] + +@[simp] +theorem insertMany_nil [EquivBEq α] [LawfulHashable α] : insertMany m [] = m := + rfl + +@[simp] +theorem insertMany_list_singleton [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + insertMany m [⟨k, v⟩] = m.insert k v := rfl + +theorem insertMany_cons [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {p : α × β} : + insertMany m (p :: l) = insertMany (m.insert p.1 p.2) l := by + rcases p with ⟨k, v⟩ + unfold insertMany + simp only [Id.pure_eq, Id.bind_eq, Id.run, List.forIn_yield_eq_foldl, List.foldl_cons] + refine Eq.trans ?_ (Eq.symm ?_ : l.foldl (fun b a => b.insert a.1 a.2) (m.insert k v) = _) + exact (List.foldl_hom (f := Subtype.val) fun x y => rfl).symm + exact (List.foldl_hom (f := Subtype.val) fun x y => rfl).symm + +private theorem insertMany_list_mk [EquivBEq α] [LawfulHashable α] + {m : DHashMap α fun _ => β} {l : List (α × β)} : + (insertMany (Quotient.mk _ m) l : ExtDHashMap α fun _ => β) = + Quotient.mk _ (DHashMap.Const.insertMany m l) := by + simp only [Quotient.mk] + induction l generalizing m with + | nil => rfl + | cons x l ih => + rcases x with ⟨k, v⟩ + simp only [insertMany_cons, DHashMap.Const.insertMany_cons, insert, + Quotient.mk', Quotient.mk, Quotient.lift, ih] + +@[elab_as_elim] +theorem insertMany_ind [EquivBEq α] [LawfulHashable α] {motive : ExtDHashMap α (fun _ => β) → Prop} + (m : ExtDHashMap α fun _ => β) (l : ρ) + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (insertMany m l) := by + change motive (Subtype.val ?my_mvar) + exact Subtype.property ?my_mvar motive init (insert _ _ _) + +@[simp] +theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + (Const.insertMany m l).contains k = (m.contains k || (l.map Prod.fst).contains k) := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.Const.contains_insertMany_list + +@[simp] +theorem mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + k ∈ insertMany m l ↔ k ∈ m ∨ (l.map Prod.fst).contains k := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.Const.mem_insertMany_list + +theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} (mem : k ∈ insertMany m l) + (contains_eq_false : (l.map Prod.fst).contains k = false) : + k ∈ m := by + refine m.inductionOn (fun _ mem contains_eq_false => ?_) mem contains_eq_false + simp only [insertMany_list_mk] at mem + exact DHashMap.Const.mem_of_mem_insertMany_list mem contains_eq_false + +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] {l : ρ} {k : α} (h' : k ∈ m) : k ∈ insertMany m l := + insertMany_ind m l h' fun _ _ _ h => mem_insert.mpr (.inr h) + +theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l).getKey? k = m.getKey? k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.Const.getKey?_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey?_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (insertMany m l).getKey? k' = some k := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.Const.getKey?_insertMany_list_of_mem k_beq distinct mem + +theorem getKey_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) + {h} : + (insertMany m l).getKey k h = + m.getKey k (mem_of_mem_insertMany_list h contains_eq_false) := by + refine m.inductionOn (fun _ contains_eq_false _ => ?_) contains_eq_false h + simp only [insertMany_list_mk] + exact DHashMap.Const.getKey_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) + {h} : + (insertMany m l).getKey k' h = k := by + refine m.inductionOn (fun _ k_beq distinct mem _ => ?_) k_beq distinct mem h + simp only [insertMany_list_mk] + exact DHashMap.Const.getKey_insertMany_list_of_mem k_beq distinct mem + +theorem getKey!_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l).getKey! k = m.getKey! k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.Const.getKey!_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey!_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (insertMany m l).getKey! k' = k := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.Const.getKey!_insertMany_list_of_mem k_beq distinct mem + +theorem getKeyD_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k fallback : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l).getKeyD k fallback = m.getKeyD k fallback := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.Const.getKeyD_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKeyD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (insertMany m l).getKeyD k' fallback = k := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.Const.getKeyD_insertMany_list_of_mem k_beq distinct mem + +theorem size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (∀ (a : α), a ∈ m → (l.map Prod.fst).contains a = false) → + (insertMany m l).size = m.size + l.length := by + refine m.inductionOn (fun _ distinct => ?_) distinct + simp only [insertMany_list_mk] + exact DHashMap.Const.size_insertMany_list distinct + +theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} : + m.size ≤ (insertMany m l).size := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.Const.size_le_size_insertMany_list + +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] {l : ρ} : m.size ≤ (insertMany m l).size := + insertMany_ind m l (Nat.le_refl _) fun _ _ _ h => Nat.le_trans h size_le_size_insert + +theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} : + (insertMany m l).size ≤ m.size + l.length := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk] + exact DHashMap.Const.size_insertMany_list_le + +@[simp] +theorem insertMany_list_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List (α × β)} : + insertMany m l = ∅ ↔ m = ∅ ∧ l = [] := by + refine m.inductionOn fun _ => ?_ + simp only [insertMany_list_mk, ← isEmpty_iff, ← List.isEmpty_iff, + Bool.coe_iff_coe, ← Bool.and_eq_true] + exact DHashMap.Const.isEmpty_insertMany_list + +theorem eq_empty_of_insertMany_eq_empty [EquivBEq α] [LawfulHashable α] {l : ρ} : insertMany m l = ∅ → m = ∅ := + insertMany_ind m l id fun _ _ _ _ h => absurd h not_insert_eq_empty + +theorem get?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + get? (insertMany m l) k = get? m k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.Const.get?_insertMany_list_of_contains_eq_false contains_eq_false + +theorem get?_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) : + get? (insertMany m l) k' = some v := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.Const.get?_insertMany_list_of_mem k_beq distinct mem + +theorem get_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) + {h} : + get (insertMany m l) k h = get m k (mem_of_mem_insertMany_list h contains_eq_false) := by + refine m.inductionOn (fun _ contains_eq_false _ => ?_) contains_eq_false h + simp only [insertMany_list_mk] + exact DHashMap.Const.get_insertMany_list_of_contains_eq_false contains_eq_false + +theorem get_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) {h} : + get (insertMany m l) k' h = v := by + refine m.inductionOn (fun _ k_beq distinct mem _ => ?_) k_beq distinct mem h + simp only [insertMany_list_mk] + exact DHashMap.Const.get_insertMany_list_of_mem k_beq distinct mem + +theorem get!_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited β] {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + get! (insertMany m l) k = get! m k := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.Const.get!_insertMany_list_of_contains_eq_false contains_eq_false + +theorem get!_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited β] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) : + get! (insertMany m l) k' = v := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.Const.get!_insertMany_list_of_mem k_beq distinct mem + +theorem getD_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} {fallback : β} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + getD (insertMany m l) k fallback = getD m k fallback := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertMany_list_mk] + exact DHashMap.Const.getD_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v fallback : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) : + getD (insertMany m l) k' fallback = v := by + refine m.inductionOn (fun _ k_beq distinct mem => ?_) k_beq distinct mem + simp only [insertMany_list_mk] + exact DHashMap.Const.getD_insertMany_list_of_mem k_beq distinct mem + +variable {m : ExtDHashMap α (fun _ => Unit)} +variable {ρ : Type w} [ForIn Id ρ α] + +@[simp] +theorem insertManyIfNewUnit_nil [EquivBEq α] [LawfulHashable α] : + insertManyIfNewUnit m [] = m := + rfl + +@[simp] +theorem insertManyIfNewUnit_list_singleton [EquivBEq α] [LawfulHashable α] {k : α} : + insertManyIfNewUnit m [k] = m.insertIfNew k () := rfl + +theorem insertManyIfNewUnit_cons [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : + insertManyIfNewUnit m (k :: l) = insertManyIfNewUnit (m.insertIfNew k ()) l := by + unfold insertManyIfNewUnit + simp only [Id.pure_eq, Id.bind_eq, Id.run, List.forIn_yield_eq_foldl, List.foldl_cons] + refine Eq.trans ?_ (Eq.symm ?_ : l.foldl (fun b a => b.insertIfNew a ()) (m.insertIfNew k ()) = _) + exact (List.foldl_hom (f := Subtype.val) fun x y => rfl).symm + exact (List.foldl_hom (f := Subtype.val) fun x y => rfl).symm + +private theorem insertManyIfNewUnit_list_mk [EquivBEq α] [LawfulHashable α] + {m : DHashMap α fun _ => Unit} {l : List α} : + (insertManyIfNewUnit (Quotient.mk _ m) l : ExtDHashMap α fun _ => Unit) = + Quotient.mk _ (DHashMap.Const.insertManyIfNewUnit m l) := by + simp only [Quotient.mk] + induction l generalizing m with + | nil => rfl + | cons x l ih => + simp only [insertManyIfNewUnit_cons, DHashMap.Const.insertManyIfNewUnit_cons, insertIfNew, + Quotient.mk', Quotient.mk, Quotient.lift, ih] + +@[elab_as_elim] +theorem insertManyIfNewUnit_ind [EquivBEq α] [LawfulHashable α] {motive : ExtDHashMap α (fun _ => Unit) → Prop} + (m : ExtDHashMap α fun _ => Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l) := by + change motive (Subtype.val ?my_mvar) + exact Subtype.property ?my_mvar motive init (insert _ _) + +@[simp] +theorem contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (insertManyIfNewUnit m l).contains k = (m.contains k || l.contains k) := by + refine m.inductionOn fun _ => ?_ + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.contains_insertManyIfNewUnit_list + +@[simp] +theorem mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + k ∈ insertManyIfNewUnit m l ↔ k ∈ m ∨ l.contains k := by + refine m.inductionOn fun _ => ?_ + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.mem_insertManyIfNewUnit_list + +theorem mem_of_mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (contains_eq_false : l.contains k = false) : + k ∈ insertManyIfNewUnit m l → k ∈ m := by + refine m.inductionOn (fun _ contains_eq_false => ?_) contains_eq_false + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.mem_of_mem_insertManyIfNewUnit_list contains_eq_false + +theorem mem_insertManyIfNewUnit_of_mem [EquivBEq α] [LawfulHashable α] {l : ρ} {k : α} (h : k ∈ m) : + k ∈ insertManyIfNewUnit m l := + insertManyIfNewUnit_ind m l h fun _ _ h => mem_insertIfNew.mpr (.inr h) + +theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getKey? (insertManyIfNewUnit m l) k = none := by + refine m.inductionOn (fun _ not_mem contains_eq_false => ?_) not_mem contains_eq_false + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false not_mem contains_eq_false + +theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getKey? (insertManyIfNewUnit m l) k' = some k := by + refine m.inductionOn (fun _ k_beq not_mem distinct mem => ?_) k_beq not_mem distinct mem + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey?_insertManyIfNewUnit_list_of_not_mem_of_mem k_beq not_mem distinct mem + +theorem getKey?_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (h' : k ∈ m) : + getKey? (insertManyIfNewUnit m l) k = getKey? m k := by + refine m.inductionOn (fun _ h' => ?_) h' + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey?_insertManyIfNewUnit_list_of_mem h' + +theorem getKey_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} + {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) {h} : + getKey (insertManyIfNewUnit m l) k' h = k := by + refine m.inductionOn (fun _ k_beq not_mem distinct mem _ => ?_) k_beq not_mem distinct mem h + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey_insertManyIfNewUnit_list_of_not_mem_of_mem k_beq not_mem distinct mem + +theorem getKey_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (mem : k ∈ m) {h} : + getKey (insertManyIfNewUnit m l) k h = getKey m k mem := by + refine m.inductionOn (fun _ mem _ => ?_) mem h + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey_insertManyIfNewUnit_list_of_mem mem + +theorem getKey!_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getKey! (insertManyIfNewUnit m l) k = default := by + refine m.inductionOn (fun _ not_mem contains_eq_false => ?_) not_mem contains_eq_false + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey!_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false not_mem contains_eq_false + +theorem getKey!_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + getKey! (insertManyIfNewUnit m l) k' = k := by + refine m.inductionOn (fun _ k_beq not_mem distinct mem => ?_) k_beq not_mem distinct mem + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey!_insertManyIfNewUnit_list_of_not_mem_of_mem k_beq not_mem distinct mem + +theorem getKey!_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} (mem : k ∈ m) : + getKey! (insertManyIfNewUnit m l) k = getKey! m k := by + refine m.inductionOn (fun _ mem => ?_) mem + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKey!_insertManyIfNewUnit_list_of_mem mem + +theorem getKeyD_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getKeyD (insertManyIfNewUnit m l) k fallback = fallback := by + refine m.inductionOn (fun _ not_mem contains_eq_false => ?_) not_mem contains_eq_false + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false not_mem contains_eq_false + +theorem getKeyD_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' fallback : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getKeyD (insertManyIfNewUnit m l) k' fallback = k := by + refine m.inductionOn (fun _ k_beq not_mem distinct mem => ?_) k_beq not_mem distinct mem + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_mem k_beq not_mem distinct mem + +theorem getKeyD_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} (mem : k ∈ m) : + getKeyD (insertManyIfNewUnit m l) k fallback = getKeyD m k fallback := by + refine m.inductionOn (fun _ mem => ?_) mem + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.getKeyD_insertManyIfNewUnit_list_of_mem mem + +theorem size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} + (distinct : l.Pairwise (fun a b => (a == b) = false)) : + (∀ (a : α), a ∈ m → l.contains a = false) → + (insertManyIfNewUnit m l).size = m.size + l.length := by + refine m.inductionOn (fun _ distinct => ?_) distinct + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.size_insertManyIfNewUnit_list distinct + +theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} : + m.size ≤ (insertManyIfNewUnit m l).size := by + refine m.inductionOn fun _ => ?_ + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.size_le_size_insertManyIfNewUnit_list + +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertManyIfNewUnit m l).size := + insertManyIfNewUnit_ind m l (Nat.le_refl _) fun _ _ h => Nat.le_trans h size_le_size_insertIfNew + +theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] + {l : List α} : + (insertManyIfNewUnit m l).size ≤ m.size + l.length := by + refine m.inductionOn fun _ => ?_ + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.size_insertManyIfNewUnit_list_le + +@[simp] +theorem insertManyIfNewUnit_list_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List α} : + insertManyIfNewUnit m l = ∅ ↔ m = ∅ ∧ l = [] := by + refine m.inductionOn fun _ => ?_ + simp only [insertManyIfNewUnit_list_mk, ← isEmpty_iff, ← List.isEmpty_iff, + Bool.coe_iff_coe, ← Bool.and_eq_true] + exact DHashMap.Const.isEmpty_insertManyIfNewUnit_list + +theorem eq_empty_of_insertManyIfNewUnit_eq_empty [EquivBEq α] [LawfulHashable α] {l : ρ} : + insertManyIfNewUnit m l = ∅ → m = ∅ := + insertManyIfNewUnit_ind m l id fun _ _ _ h => absurd h not_insertIfNew_eq_empty + +theorem get?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + get? (insertManyIfNewUnit m l) k = + if k ∈ m ∨ l.contains k then some () else none := by + refine m.inductionOn fun _ => ?_ + simp only [insertManyIfNewUnit_list_mk] + exact DHashMap.Const.get?_insertManyIfNewUnit_list + +theorem get_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {h} : + get (insertManyIfNewUnit m l) k h = () := + rfl + +theorem get!_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + get! (insertManyIfNewUnit m l) k = () := + rfl + +theorem getD_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {fallback : Unit} : + getD (insertManyIfNewUnit m l) k fallback = () := + rfl + +end Const + +end insertMany + +end ExtDHashMap + +namespace ExtDHashMap + +@[simp] +theorem ofList_nil [EquivBEq α] [LawfulHashable α] : + ofList ([] : List ((a : α) × β a)) = ∅ := rfl + +@[simp] +theorem ofList_singleton [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} : + ofList [⟨k, v⟩] = (∅ : ExtDHashMap α β).insert k v := rfl + +theorem ofList_cons [EquivBEq α] [LawfulHashable α] {k : α} {v : β k} {tl : List ((a : α) × β a)} : + ofList (⟨k, v⟩ :: tl) = ((∅ : ExtDHashMap α β).insert k v).insertMany tl := by + conv => rhs; apply insertMany_list_mk + exact congrArg Quotient.mk' DHashMap.ofList_cons + +@[simp] +theorem contains_ofList [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} : + (ofList l).contains k = (l.map Sigma.fst).contains k := + DHashMap.contains_ofList + +@[simp] +theorem mem_ofList [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} : + k ∈ ofList l ↔ (l.map Sigma.fst).contains k := + DHashMap.mem_ofList + +theorem get?_ofList_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (ofList l).get? k = none := + DHashMap.get?_ofList_of_contains_eq_false contains_eq_false + +theorem get?_ofList_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (ofList l).get? k' = some (cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v) := + DHashMap.get?_ofList_of_mem k_beq distinct mem + +theorem get_ofList_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) + {h} : + (ofList l).get k' h = cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v := + DHashMap.get_ofList_of_mem k_beq distinct mem + +theorem get!_ofList_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} [Inhabited (β k)] + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (ofList l).get! k = default := + DHashMap.get!_ofList_of_contains_eq_false contains_eq_false + +theorem get!_ofList_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} [Inhabited (β k')] + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (ofList l).get! k' = cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v := + DHashMap.get!_ofList_of_mem k_beq distinct mem + +theorem getD_ofList_of_contains_eq_false [LawfulBEq α] + {l : List ((a : α) × β a)} {k : α} {fallback : β k} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (ofList l).getD k fallback = fallback := + DHashMap.getD_ofList_of_contains_eq_false contains_eq_false + +theorem getD_ofList_of_mem [LawfulBEq α] + {l : List ((a : α) × β a)} {k k' : α} (k_beq : k == k') {v : β k} {fallback : β k'} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (ofList l).getD k' fallback = cast (by congr; apply LawfulBEq.eq_of_beq k_beq) v := + DHashMap.getD_ofList_of_mem k_beq distinct mem + +theorem getKey?_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (ofList l).getKey? k = none := + DHashMap.getKey?_ofList_of_contains_eq_false contains_eq_false + +theorem getKey?_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) : + (ofList l).getKey? k' = some k := + DHashMap.getKey?_ofList_of_mem k_beq distinct mem + +theorem getKey_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) + {h} : + (ofList l).getKey k' h = k := + DHashMap.getKey_ofList_of_mem k_beq distinct mem + +theorem getKey!_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List ((a : α) × β a)} {k : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (ofList l).getKey! k = default := + DHashMap.getKey!_ofList_of_contains_eq_false contains_eq_false + +theorem getKey!_ofList_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List ((a : α) × β a)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) : + (ofList l).getKey! k' = k := + DHashMap.getKey!_ofList_of_mem k_beq distinct mem + +theorem getKeyD_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} {k fallback : α} + (contains_eq_false : (l.map Sigma.fst).contains k = false) : + (ofList l).getKeyD k fallback = fallback := + DHashMap.getKeyD_ofList_of_contains_eq_false contains_eq_false + +theorem getKeyD_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} + {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Sigma.fst) : + (ofList l).getKeyD k' fallback = k := + DHashMap.getKeyD_ofList_of_mem k_beq distinct mem + +theorem size_ofList [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (ofList l).size = l.length := + DHashMap.size_ofList distinct + +theorem size_ofList_le [EquivBEq α] [LawfulHashable α] + {l : List ((a : α) × β a)} : + (ofList l).size ≤ l.length := + DHashMap.size_ofList_le + +@[simp] +theorem ofList_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List ((a : α) × β a)} : + ofList l = ∅ ↔ l = [] := by + simpa only [← isEmpty_iff, ← List.isEmpty_iff, Bool.coe_iff_coe] using + DHashMap.isEmpty_ofList + +namespace Const + +variable {β : Type v} + +@[simp] +theorem ofList_nil [EquivBEq α] [LawfulHashable α] : + ofList ([] : List (α × β)) = ∅ := + rfl + +@[simp] +theorem ofList_singleton [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + ofList [⟨k, v⟩] = (∅ : ExtDHashMap α (fun _ => β)).insert k v := + rfl + +theorem ofList_cons [EquivBEq α] [LawfulHashable α] {k : α} {v : β} {tl : List (α × β)} : + ofList (⟨k, v⟩ :: tl) = insertMany ((∅ : ExtDHashMap α (fun _ => β)).insert k v) tl := by + conv => rhs; apply insertMany_list_mk + exact congrArg Quotient.mk' DHashMap.Const.ofList_cons + +@[simp] +theorem contains_ofList [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + (ofList l).contains k = (l.map Prod.fst).contains k := + DHashMap.Const.contains_ofList + +@[simp] +theorem mem_ofList [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + k ∈ ofList l ↔ (l.map Prod.fst).contains k := + DHashMap.Const.mem_ofList + +theorem get?_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + get? (ofList l) k = none := + DHashMap.Const.get?_ofList_of_contains_eq_false contains_eq_false + +theorem get?_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + get? (ofList l) k' = some v := + DHashMap.Const.get?_ofList_of_mem k_beq distinct mem + +theorem get_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) + {h} : + get (ofList l) k' h = v := + DHashMap.Const.get_ofList_of_mem k_beq distinct mem + +theorem get!_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} [Inhabited β] + (contains_eq_false : (l.map Prod.fst).contains k = false) : + get! (ofList l) k = (default : β) := + DHashMap.Const.get!_ofList_of_contains_eq_false contains_eq_false + +theorem get!_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} [Inhabited β] + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + get! (ofList l) k' = v := + DHashMap.Const.get!_ofList_of_mem k_beq distinct mem + +theorem getD_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} {fallback : β} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + getD (ofList l) k fallback = fallback := + DHashMap.Const.getD_ofList_of_contains_eq_false contains_eq_false + +theorem getD_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} {fallback : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + getD (ofList l) k' fallback = v := + DHashMap.Const.getD_ofList_of_mem k_beq distinct mem + +theorem getKey?_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l).getKey? k = none := + DHashMap.Const.getKey?_ofList_of_contains_eq_false contains_eq_false + +theorem getKey?_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (ofList l).getKey? k' = some k := + DHashMap.Const.getKey?_ofList_of_mem k_beq distinct mem + +theorem getKey_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) + {h} : + (ofList l).getKey k' h = k := + DHashMap.Const.getKey_ofList_of_mem k_beq distinct mem + +theorem getKey!_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l).getKey! k = default := + DHashMap.Const.getKey!_ofList_of_contains_eq_false contains_eq_false + +theorem getKey!_ofList_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (ofList l).getKey! k' = k := + DHashMap.Const.getKey!_ofList_of_mem k_beq distinct mem + +theorem getKeyD_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k fallback : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l).getKeyD k fallback = fallback := + DHashMap.Const.getKeyD_ofList_of_contains_eq_false contains_eq_false + +theorem getKeyD_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (ofList l).getKeyD k' fallback = k := + DHashMap.Const.getKeyD_ofList_of_mem k_beq distinct mem + +theorem size_ofList [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (ofList l).size = l.length := + DHashMap.Const.size_ofList distinct + +theorem size_ofList_le [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} : + (ofList l).size ≤ l.length := + DHashMap.Const.size_ofList_le + +@[simp] +theorem ofList_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List (α × β)} : + ofList l = ∅ ↔ l = [] := by + simpa only [← isEmpty_iff, ← List.isEmpty_iff, Bool.coe_iff_coe] using + DHashMap.Const.isEmpty_ofList + +@[simp] +theorem unitOfList_nil [EquivBEq α] [LawfulHashable α] : + unitOfList ([] : List α) = ∅ := + congrArg Quotient.mk' DHashMap.Const.unitOfList_nil + +@[simp] +theorem unitOfList_singleton [EquivBEq α] [LawfulHashable α] {k : α} : + unitOfList [k] = (∅ : ExtDHashMap α (fun _ => Unit)).insertIfNew k () := + congrArg Quotient.mk' DHashMap.Const.unitOfList_singleton + +theorem unitOfList_cons [EquivBEq α] [LawfulHashable α] {hd : α} {tl : List α} : + unitOfList (hd :: tl) = + insertManyIfNewUnit ((∅ : ExtDHashMap α (fun _ => Unit)).insertIfNew hd ()) tl := by + conv => rhs; apply insertManyIfNewUnit_list_mk + exact congrArg Quotient.mk' DHashMap.Const.unitOfList_cons + +@[simp] +theorem contains_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (unitOfList l).contains k = l.contains k := + DHashMap.Const.contains_unitOfList + +@[simp] +theorem mem_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + k ∈ unitOfList l ↔ l.contains k := + DHashMap.Const.mem_unitOfList + +theorem getKey?_unitOfList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (contains_eq_false : l.contains k = false) : + getKey? (unitOfList l) k = none := + DHashMap.Const.getKey?_unitOfList_of_contains_eq_false contains_eq_false + +theorem getKey?_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getKey? (unitOfList l) k' = some k := + DHashMap.Const.getKey?_unitOfList_of_mem k_beq distinct mem + +theorem getKey_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) {h} : + getKey (unitOfList l) k' h = k := + DHashMap.Const.getKey_unitOfList_of_mem k_beq distinct mem + +theorem getKey!_unitOfList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} + (contains_eq_false : l.contains k = false) : + getKey! (unitOfList l) k = default := + DHashMap.Const.getKey!_unitOfList_of_contains_eq_false contains_eq_false + +theorem getKey!_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + getKey! (unitOfList l) k' = k := + DHashMap.Const.getKey!_unitOfList_of_mem k_beq distinct mem + +theorem getKeyD_unitOfList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} + (contains_eq_false : l.contains k = false) : + getKeyD (unitOfList l) k fallback = fallback := + DHashMap.Const.getKeyD_unitOfList_of_contains_eq_false contains_eq_false + +theorem getKeyD_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + getKeyD (unitOfList l) k' fallback = k := + DHashMap.Const.getKeyD_unitOfList_of_mem k_beq distinct mem + +theorem size_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} + (distinct : l.Pairwise (fun a b => (a == b) = false)) : + (unitOfList l).size = l.length := + DHashMap.Const.size_unitOfList distinct + +theorem size_unitOfList_le [EquivBEq α] [LawfulHashable α] + {l : List α} : + (unitOfList l).size ≤ l.length := + DHashMap.Const.size_unitOfList_le + +@[simp] +theorem unitOfList_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List α} : + unitOfList l = ∅ ↔ l = [] := by + simpa only [← isEmpty_iff, ← List.isEmpty_iff, Bool.coe_iff_coe] using + DHashMap.Const.isEmpty_unitOfList + +@[simp] +theorem get?_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + get? (unitOfList l) k = + if l.contains k then some () else none := + DHashMap.Const.get?_unitOfList + +@[simp] +theorem get_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {h} : + get (unitOfList l) k h = () := + DHashMap.Const.get_unitOfList + +@[simp] +theorem get!_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + get! (unitOfList l) k = () := + DHashMap.Const.get!_unitOfList + +@[simp] +theorem getD_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {fallback : Unit} : + getD (unitOfList l) k fallback = () := + DHashMap.Const.getD_unitOfList + +end Const + +variable {m : ExtDHashMap α β} + +section Alter + +theorem alter_eq_empty_iff_erase_eq_empty [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + m.alter k f = ∅ ↔ m.erase k = ∅ ∧ f (m.get? k) = none := by + rcases m with ⟨m⟩ + simp only [← isEmpty_iff] + dsimp only [isEmpty, alter, Quotient.lift, Quotient.mk', Quotient.mk] + simp only [DHashMap.isEmpty_alter_eq_isEmpty_erase, Bool.and_eq_true, Option.isNone_iff_eq_none] + rfl + +@[simp] +theorem alter_eq_empty_iff [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + alter m k f = ∅ ↔ (m = ∅ ∨ (m.size = 1 ∧ k ∈ m)) ∧ f (get? m k) = none := by + simp only [alter_eq_empty_iff_erase_eq_empty, erase_eq_empty_iff] + +theorem contains_alter [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).contains k' = if k == k' then (f (m.get? k)).isSome else m.contains k' := + m.inductionOn fun _ => DHashMap.contains_alter + +theorem mem_alter [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} : + k' ∈ m.alter k f ↔ if k == k' then (f (m.get? k)).isSome = true else k' ∈ m := + m.inductionOn fun _ => DHashMap.mem_alter + +theorem mem_alter_of_beq [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} (h : k == k') : + k' ∈ m.alter k f ↔ (f (m.get? k)).isSome := + m.inductionOn (fun _ h => DHashMap.mem_alter_of_beq h) h + +@[simp] +theorem contains_alter_self [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).contains k = (f (m.get? k)).isSome := + m.inductionOn fun _ => DHashMap.contains_alter_self + +@[simp] +theorem mem_alter_self [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + k ∈ m.alter k f ↔ (f (m.get? k)).isSome := + m.inductionOn fun _ => DHashMap.mem_alter_self + +theorem contains_alter_of_beq_eq_false [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} + (h : (k == k') = false) : (m.alter k f).contains k' = m.contains k' := + m.inductionOn (fun _ h => DHashMap.contains_alter_of_beq_eq_false h) h + +theorem mem_alter_of_beq_eq_false [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} + (h : (k == k') = false) : k' ∈ m.alter k f ↔ k' ∈ m := + m.inductionOn (fun _ h => DHashMap.mem_alter_of_beq_eq_false h) h + +theorem size_alter [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).size = + if k ∈ m ∧ (f (m.get? k)).isNone then + m.size - 1 + else if k ∉ m ∧ (f (m.get? k)).isSome then + m.size + 1 + else + m.size := + m.inductionOn fun _ => DHashMap.size_alter + +theorem size_alter_eq_add_one [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} + (h : k ∉ m) (h' : (f (m.get? k)).isSome) : + (m.alter k f).size = m.size + 1 := + m.inductionOn (fun _ h h' => DHashMap.size_alter_eq_add_one h h') h h' + +theorem size_alter_eq_sub_one [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} + (h : k ∈ m) (h' : (f (m.get? k)).isNone) : + (m.alter k f).size = m.size - 1 := + m.inductionOn (fun _ h h' => DHashMap.size_alter_eq_sub_one h h') h h' + +theorem size_alter_eq_self_of_not_mem [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} + (h : k ∉ m) (h' : (f (m.get? k)).isNone) : (m.alter k f).size = m.size := + m.inductionOn (fun _ h h' => DHashMap.size_alter_eq_self_of_not_mem h h') h h' + +theorem size_alter_eq_self_of_mem [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} + (h : k ∈ m) (h' : (f (m.get? k)).isSome) : (m.alter k f).size = m.size := + m.inductionOn (fun _ h h' => DHashMap.size_alter_eq_self_of_mem h h') h h' + +theorem size_alter_le_size [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).size ≤ m.size + 1 := + m.inductionOn fun _ => DHashMap.size_alter_le_size + +theorem size_le_size_alter [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + m.size - 1 ≤ (m.alter k f).size := + m.inductionOn fun _ => DHashMap.size_le_size_alter + +theorem get?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).get? k' = + if h : k == k' then + (cast (congrArg (Option ∘ β) (eq_of_beq h)) (f (m.get? k))) + else + m.get? k' := + m.inductionOn fun _ => DHashMap.get?_alter + +@[simp] +theorem get?_alter_self [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).get? k = f (m.get? k) := + m.inductionOn fun _ => DHashMap.get?_alter_self + +theorem get_alter [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} + {h : k' ∈ m.alter k f} : + (m.alter k f).get k' h = + if heq : k == k' then + haveI h' : (f (m.get? k)).isSome := mem_alter_of_beq heq |>.mp h + cast (congrArg β (eq_of_beq heq)) <| (f (m.get? k)).get <| h' + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + m.get k' h' := + m.inductionOn (fun _ _ => DHashMap.get_alter) h + +@[simp] +theorem get_alter_self [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} + {h : k ∈ m.alter k f} : + haveI h' : (f (m.get? k)).isSome := mem_alter_self.mp h + (m.alter k f).get k h = (f (m.get? k)).get h' := + m.inductionOn (fun _ _ => DHashMap.get_alter_self) h + +theorem get!_alter [LawfulBEq α] {k k' : α} [hi : Inhabited (β k')] + {f : Option (β k) → Option (β k)} : (m.alter k f).get! k' = + if heq : k == k' then + (f (m.get? k)).map (cast (congrArg β (eq_of_beq heq))) |>.get! + else + m.get! k' := + m.inductionOn fun _ => DHashMap.get!_alter + +@[simp] +theorem get!_alter_self [LawfulBEq α] {k : α} [Inhabited (β k)] {f : Option (β k) → Option (β k)} : + (m.alter k f).get! k = (f (m.get? k)).get! := + m.inductionOn fun _ => DHashMap.get!_alter_self + +theorem getD_alter [LawfulBEq α] {k k' : α} {fallback : β k'} {f : Option (β k) → Option (β k)} : + (m.alter k f).getD k' fallback = + if heq : k == k' then + f (m.get? k) |>.map (cast (congrArg β <| eq_of_beq heq)) |>.getD fallback + else + m.getD k' fallback := + m.inductionOn fun _ => DHashMap.getD_alter + +@[simp] +theorem getD_alter_self [LawfulBEq α] {k : α} {fallback : β k} {f : Option (β k) → Option (β k)} : + (m.alter k f).getD k fallback = (f (m.get? k)).getD fallback := + m.inductionOn fun _ => DHashMap.getD_alter_self + +theorem getKey?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).getKey? k' = + if k == k' then + if (f (m.get? k)).isSome then some k else none + else + m.getKey? k' := + m.inductionOn fun _ => DHashMap.getKey?_alter + +theorem getKey?_alter_self [LawfulBEq α] {k : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).getKey? k = if (f (m.get? k)).isSome then some k else none := + m.inductionOn fun _ => DHashMap.getKey?_alter_self + +theorem getKey!_alter [LawfulBEq α] [Inhabited α] {k k' : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).getKey! k' = + if k == k' then + if (f (m.get? k)).isSome then k else default + else + m.getKey! k' := + m.inductionOn fun _ => DHashMap.getKey!_alter + +theorem getKey!_alter_self [LawfulBEq α] [Inhabited α] {k : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).getKey! k = if (f (m.get? k)).isSome then k else default := + m.inductionOn fun _ => DHashMap.getKey!_alter_self + +theorem getKey_alter [LawfulBEq α] [Inhabited α] {k k' : α} {f : Option (β k) → Option (β k)} + {h : k' ∈ m.alter k f} : + (m.alter k f).getKey k' h = + if heq : k == k' then + k + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + m.getKey k' h' := + m.inductionOn (fun _ _ => DHashMap.getKey_alter) h + +@[simp] +theorem getKey_alter_self [LawfulBEq α] [Inhabited α] {k : α} {f : Option (β k) → Option (β k)} + {h : k ∈ m.alter k f} : (m.alter k f).getKey k h = k := + m.inductionOn (fun _ _ => DHashMap.getKey_alter_self) h + +theorem getKeyD_alter [LawfulBEq α] {k k' fallback : α} {f : Option (β k) → Option (β k)} : + (m.alter k f).getKeyD k' fallback = + if k == k' then + if (f (m.get? k)).isSome then k else fallback + else + m.getKeyD k' fallback := + m.inductionOn fun _ => DHashMap.getKeyD_alter + +@[simp] +theorem getKeyD_alter_self [LawfulBEq α] [Inhabited α] {k : α} {fallback : α} + {f : Option (β k) → Option (β k)} : + (m.alter k f).getKeyD k fallback = if (f (m.get? k)).isSome then k else fallback := + m.inductionOn fun _ => DHashMap.getKeyD_alter_self + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +theorem alter_eq_empty_iff_erase_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + alter m k f = ∅ ↔ m.erase k = ∅ ∧ f (get? m k) = none := by + rcases m with ⟨m⟩ + simp only [← isEmpty_iff] + dsimp only [isEmpty, alter, Quotient.lift, Quotient.mk', Quotient.mk] + simp only [DHashMap.Const.isEmpty_alter_eq_isEmpty_erase, Bool.and_eq_true, + Option.isNone_iff_eq_none] + rfl + +@[simp] +theorem alter_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + alter m k f = ∅ ↔ (m = ∅ ∨ (m.size = 1 ∧ k ∈ m)) ∧ f (get? m k) = none := by + simp only [alter_eq_empty_iff_erase_eq_empty, erase_eq_empty_iff] + +theorem contains_alter [EquivBEq α] [LawfulHashable α] {k k': α} {f : Option β → Option β} : + (Const.alter m k f).contains k' = + if k == k' then (f (Const.get? m k)).isSome else m.contains k' := + m.inductionOn fun _ => DHashMap.Const.contains_alter + +theorem mem_alter [EquivBEq α] [LawfulHashable α] {k k': α} {f : Option β → Option β} : + k' ∈ Const.alter m k f ↔ if k == k' then (f (Const.get? m k)).isSome = true else k' ∈ m := + m.inductionOn fun _ => DHashMap.Const.mem_alter + +theorem mem_alter_of_beq [EquivBEq α] [LawfulHashable α] {k k': α} {f : Option β → Option β} + (h : k == k') : k' ∈ Const.alter m k f ↔ (f (Const.get? m k)).isSome := + m.inductionOn (fun _ h => DHashMap.Const.mem_alter_of_beq h) h + +@[simp] +theorem contains_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (Const.alter m k f).contains k = (f (Const.get? m k)).isSome := + m.inductionOn fun _ => DHashMap.Const.contains_alter_self + +@[simp] +theorem mem_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + k ∈ Const.alter m k f ↔ (f (Const.get? m k)).isSome := + m.inductionOn fun _ => DHashMap.Const.mem_alter_self + +theorem contains_alter_of_beq_eq_false [EquivBEq α] [LawfulHashable α] {k k' : α} + {f : Option β → Option β} (h : (k == k') = false) : + (Const.alter m k f).contains k' = m.contains k' := + m.inductionOn (fun _ h => DHashMap.Const.contains_alter_of_beq_eq_false h) h + +theorem mem_alter_of_beq_eq_false [EquivBEq α] [LawfulHashable α] {k k' : α} + {f : Option β → Option β} (h : (k == k') = false) : k' ∈ Const.alter m k f ↔ k' ∈ m := + m.inductionOn (fun _ h => DHashMap.Const.mem_alter_of_beq_eq_false h) h + +theorem size_alter [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (Const.alter m k f).size = + if k ∈ m ∧ (f (Const.get? m k)).isNone then + m.size - 1 + else if k ∉ m ∧ (f (Const.get? m k)).isSome then + m.size + 1 + else + m.size := + m.inductionOn fun _ => DHashMap.Const.size_alter + +theorem size_alter_eq_add_one [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∉ m) (h' : (f (Const.get? m k)).isSome) : + (Const.alter m k f).size = m.size + 1 := + m.inductionOn (fun _ h h' => DHashMap.Const.size_alter_eq_add_one h h') h h' + +theorem size_alter_eq_sub_one [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∈ m) (h' : (f (Const.get? m k)).isNone) : + (Const.alter m k f).size = m.size - 1 := + m.inductionOn (fun _ h h' => DHashMap.Const.size_alter_eq_sub_one h h') h h' + +theorem size_alter_eq_self_of_not_mem [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∉ m) (h' : (f (Const.get? m k)).isNone) : (Const.alter m k f).size = m.size := + m.inductionOn (fun _ h h' => DHashMap.Const.size_alter_eq_self_of_not_mem h h') h h' + +theorem size_alter_eq_self_of_mem [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∈ m) (h' : (f (Const.get? m k)).isSome) : (Const.alter m k f).size = m.size := + m.inductionOn (fun _ h h' => DHashMap.Const.size_alter_eq_self_of_mem h h') h h' + +theorem size_alter_le_size [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (Const.alter m k f).size ≤ m.size + 1 := + m.inductionOn fun _ => DHashMap.Const.size_alter_le_size + +theorem size_le_size_alter [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + m.size - 1 ≤ (Const.alter m k f).size := + m.inductionOn fun _ => DHashMap.Const.size_le_size_alter + +theorem get?_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} : + Const.get? (Const.alter m k f) k' = + if k == k' then + f (Const.get? m k) + else + Const.get? m k' := + m.inductionOn fun _ => DHashMap.Const.get?_alter + +@[simp] +theorem get?_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + Const.get? (Const.alter m k f) k = f (Const.get? m k) := + m.inductionOn fun _ => DHashMap.Const.get?_alter_self + +theorem get_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} + {h : k' ∈ Const.alter m k f} : + Const.get (Const.alter m k f) k' h = + if heq : k == k' then + haveI h' : (f (Const.get? m k)).isSome := mem_alter_of_beq heq |>.mp h + f (Const.get? m k) |>.get h' + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + Const.get m k' h' := + m.inductionOn (fun _ _ => DHashMap.Const.get_alter) h + +@[simp] +theorem get_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + {h : k ∈ Const.alter m k f} : + haveI h' : (f (Const.get? m k)).isSome := mem_alter_self.mp h + Const.get (Const.alter m k f) k h = (f (Const.get? m k)).get h' := + m.inductionOn (fun _ _ => DHashMap.Const.get_alter_self) h + +theorem get!_alter [EquivBEq α] [LawfulHashable α] {k k' : α} [Inhabited β] + {f : Option β → Option β} : Const.get! (Const.alter m k f) k' = + if k == k' then + f (Const.get? m k) |>.get! + else + Const.get! m k' := + m.inductionOn fun _ => DHashMap.Const.get!_alter + +@[simp] +theorem get!_alter_self [EquivBEq α] [LawfulHashable α] {k : α} [Inhabited β] + {f : Option β → Option β} : Const.get! (Const.alter m k f) k = (f (Const.get? m k)).get! := + m.inductionOn fun _ => DHashMap.Const.get!_alter_self + +theorem getD_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {fallback : β} + {f : Option β → Option β} : + Const.getD (Const.alter m k f) k' fallback = + if k == k' then + f (Const.get? m k) |>.getD fallback + else + Const.getD m k' fallback := + m.inductionOn fun _ => DHashMap.Const.getD_alter + +@[simp] +theorem getD_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} + {f : Option β → Option β} : + Const.getD (Const.alter m k f) k fallback = (f (Const.get? m k)).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_alter_self + +theorem getKey?_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} : + (Const.alter m k f).getKey? k' = + if k == k' then + if (f (Const.get? m k)).isSome then some k else none + else + m.getKey? k' := + m.inductionOn fun _ => DHashMap.Const.getKey?_alter + +theorem getKey?_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (Const.alter m k f).getKey? k = if (f (Const.get? m k)).isSome then some k else none := + m.inductionOn fun _ => DHashMap.Const.getKey?_alter_self + +theorem getKey!_alter [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} + {f : Option β → Option β} : (Const.alter m k f).getKey! k' = + if k == k' then + if (f (Const.get? m k)).isSome then k else default + else + m.getKey! k' := + m.inductionOn fun _ => DHashMap.Const.getKey!_alter + +theorem getKey!_alter_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} + {f : Option β → Option β} : + (Const.alter m k f).getKey! k = if (f (Const.get? m k)).isSome then k else default := + m.inductionOn fun _ => DHashMap.Const.getKey!_alter_self + +theorem getKey_alter [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} + {f : Option β → Option β} {h : k' ∈ Const.alter m k f} : + (Const.alter m k f).getKey k' h = + if heq : k == k' then + k + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + m.getKey k' h' := + m.inductionOn (fun _ _ => DHashMap.Const.getKey_alter) h + +@[simp] +theorem getKey_alter_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} + {f : Option β → Option β} {h : k ∈ Const.alter m k f} : + (Const.alter m k f).getKey k h = k := + m.inductionOn (fun _ _ => DHashMap.Const.getKey_alter_self) h + +theorem getKeyD_alter [EquivBEq α] [LawfulHashable α] {k k' fallback : α} {f : Option β → Option β} : + (Const.alter m k f).getKeyD k' fallback = + if k == k' then + if (f (Const.get? m k)).isSome then k else fallback + else + m.getKeyD k' fallback := + m.inductionOn fun _ => DHashMap.Const.getKeyD_alter + +theorem getKeyD_alter_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k fallback : α} + {f : Option β → Option β} : + (Const.alter m k f).getKeyD k fallback = + if (f (Const.get? m k)).isSome then k else fallback := + m.inductionOn fun _ => DHashMap.Const.getKeyD_alter_self + +end Const + +end Alter + +section Modify + +@[simp] +theorem modify_eq_empty_iff [LawfulBEq α] {k : α} {f : β k → β k} : + m.modify k f = ∅ ↔ m = ∅ := by + simp only [← isEmpty_iff, Bool.coe_iff_coe] + exact m.inductionOn fun _ => DHashMap.isEmpty_modify + +@[simp] +theorem contains_modify [LawfulBEq α] {k k': α} {f : β k → β k} : + (m.modify k f).contains k' = m.contains k' := + m.inductionOn fun _ => DHashMap.contains_modify + +@[simp] +theorem mem_modify [LawfulBEq α] {k k': α} {f : β k → β k} : k' ∈ m.modify k f ↔ k' ∈ m := + m.inductionOn fun _ => DHashMap.mem_modify + +@[simp] +theorem size_modify [LawfulBEq α] {k : α} {f : β k → β k} : (m.modify k f).size = m.size := + m.inductionOn fun _ => DHashMap.size_modify + +theorem get?_modify [LawfulBEq α] {k k' : α} {f : β k → β k} : + (m.modify k f).get? k' = if h : k == k' then + (cast (congrArg (Option ∘ β) (eq_of_beq h)) ((m.get? k).map f)) + else + m.get? k' := + m.inductionOn fun _ => DHashMap.get?_modify + +@[simp] +theorem get?_modify_self [LawfulBEq α] {k : α} {f : β k → β k} : + (m.modify k f).get? k = (m.get? k).map f := + m.inductionOn fun _ => DHashMap.get?_modify_self + +theorem get_modify [LawfulBEq α] {k k' : α} {f : β k → β k} + (h : k' ∈ m.modify k f) : + (m.modify k f).get k' h = + if heq : k == k' then + haveI h' : k ∈ m := mem_congr heq |>.mpr <| mem_modify.mp h + cast (congrArg β (eq_of_beq heq)) <| f (m.get k h') + else + haveI h' : k' ∈ m := mem_modify.mp h + m.get k' h' := + m.inductionOn (fun _ h => DHashMap.get_modify h) h + +@[simp] +theorem get_modify_self [LawfulBEq α] {k : α} {f : β k → β k} {h : k ∈ m.modify k f} : + haveI h' : k ∈ m := mem_modify.mp h + (m.modify k f).get k h = f (m.get k h') := + m.inductionOn (fun _ _ => DHashMap.get_modify_self) h + +theorem get!_modify [LawfulBEq α] {k k' : α} [hi : Inhabited (β k')] {f : β k → β k} : + (m.modify k f).get! k' = + if heq : k == k' then + m.get? k |>.map f |>.map (cast (congrArg β (eq_of_beq heq))) |>.get! + else + m.get! k' := + m.inductionOn fun _ => DHashMap.get!_modify + +@[simp] +theorem get!_modify_self [LawfulBEq α] {k : α} [Inhabited (β k)] {f : β k → β k} : + (m.modify k f).get! k = ((m.get? k).map f).get! := + m.inductionOn fun _ => DHashMap.get!_modify_self + +theorem getD_modify [LawfulBEq α] {k k' : α} {fallback : β k'} {f : β k → β k} : + (m.modify k f).getD k' fallback = + if heq : k == k' then + m.get? k |>.map f |>.map (cast (congrArg β <| eq_of_beq heq)) |>.getD fallback + else + m.getD k' fallback := + m.inductionOn fun _ => DHashMap.getD_modify + +@[simp] +theorem getD_modify_self [LawfulBEq α] {k : α} {fallback : β k} {f : β k → β k} : + (m.modify k f).getD k fallback = ((m.get? k).map f).getD fallback := + m.inductionOn fun _ => DHashMap.getD_modify_self + +theorem getKey?_modify [LawfulBEq α] {k k' : α} {f : β k → β k} : + (m.modify k f).getKey? k' = + if k == k' then + if k ∈ m then some k else none + else + m.getKey? k' := + m.inductionOn fun _ => DHashMap.getKey?_modify + +theorem getKey?_modify_self [LawfulBEq α] {k : α} {f : β k → β k} : + (m.modify k f).getKey? k = if k ∈ m then some k else none := + m.inductionOn fun _ => DHashMap.getKey?_modify_self + +theorem getKey!_modify [LawfulBEq α] [Inhabited α] {k k' : α} {f : β k → β k} : + (m.modify k f).getKey! k' = + if k == k' then + if k ∈ m then k else default + else + m.getKey! k' := + m.inductionOn fun _ => DHashMap.getKey!_modify + +theorem getKey!_modify_self [LawfulBEq α] [Inhabited α] {k : α} {f : β k → β k} : + (m.modify k f).getKey! k = if k ∈ m then k else default := + m.inductionOn fun _ => DHashMap.getKey!_modify_self + +theorem getKey_modify [LawfulBEq α] [Inhabited α] {k k' : α} {f : β k → β k} + {h : k' ∈ m.modify k f} : + (m.modify k f).getKey k' h = + if k == k' then + k + else + haveI h' : k' ∈ m := mem_modify.mp h + m.getKey k' h' := + m.inductionOn (fun _ _ => DHashMap.getKey_modify) h + +@[simp] +theorem getKey_modify_self [LawfulBEq α] [Inhabited α] {k : α} {f : β k → β k} + {h : k ∈ m.modify k f} : (m.modify k f).getKey k h = k := + m.inductionOn (fun _ _ => DHashMap.getKey_modify_self) h + +theorem getKeyD_modify [LawfulBEq α] {k k' fallback : α} {f : β k → β k} : + (m.modify k f).getKeyD k' fallback = + if k == k' then + if k ∈ m then k else fallback + else + m.getKeyD k' fallback := + m.inductionOn fun _ => DHashMap.getKeyD_modify + +theorem getKeyD_modify_self [LawfulBEq α] [Inhabited α] {k fallback : α} {f : β k → β k} : + (m.modify k f).getKeyD k fallback = if k ∈ m then k else fallback := + m.inductionOn fun _ => DHashMap.getKeyD_modify_self + +namespace Const + +variable {β : Type v} {m : ExtDHashMap α (fun _ => β)} + +@[simp] +theorem modify_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + Const.modify m k f = ∅ ↔ m = ∅ := by + simp only [← isEmpty_iff, Bool.coe_iff_coe] + exact m.inductionOn fun _ => DHashMap.Const.isEmpty_modify + +@[simp] +theorem contains_modify [EquivBEq α] [LawfulHashable α] {k k': α} {f : β → β} : + (Const.modify m k f).contains k' = m.contains k' := + m.inductionOn fun _ => DHashMap.Const.contains_modify + +@[simp] +theorem mem_modify [EquivBEq α] [LawfulHashable α] {k k': α} {f : β → β} : + k' ∈ Const.modify m k f ↔ k' ∈ m := + m.inductionOn fun _ => DHashMap.Const.mem_modify + +@[simp] +theorem size_modify [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + (Const.modify m k f).size = m.size := + m.inductionOn fun _ => DHashMap.Const.size_modify + +theorem get?_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} : + Const.get? (Const.modify m k f) k' = if k == k' then + Const.get? m k |>.map f + else + Const.get? m k' := + m.inductionOn fun _ => DHashMap.Const.get?_modify + +@[simp] +theorem get?_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + Const.get? (Const.modify m k f) k = (Const.get? m k).map f := + m.inductionOn fun _ => DHashMap.Const.get?_modify_self + +theorem get_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} + {h : k' ∈ Const.modify m k f} : + Const.get (Const.modify m k f) k' h = + if heq : k == k' then + haveI h' : k ∈ m := mem_congr heq |>.mpr <| mem_modify.mp h + f (Const.get m k h') + else + haveI h' : k' ∈ m := mem_modify.mp h + Const.get m k' h' := + m.inductionOn (fun _ _ => DHashMap.Const.get_modify) h + +@[simp] +theorem get_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} + {h : k ∈ Const.modify m k f} : + haveI h' : k ∈ m := mem_modify.mp h + Const.get (Const.modify m k f) k h = f (Const.get m k h') := + m.inductionOn (fun _ _ => DHashMap.Const.get_modify_self) h + +theorem get!_modify [EquivBEq α] [LawfulHashable α] {k k' : α} [Inhabited β] {f : β → β} : + Const.get! (Const.modify m k f) k' = + if k == k' then + Const.get? m k |>.map f |>.get! + else + Const.get! m k' := + m.inductionOn fun _ => DHashMap.Const.get!_modify + +@[simp] +theorem get!_modify_self [EquivBEq α] [LawfulHashable α] {k : α} [Inhabited β] {f : β → β} : + Const.get! (Const.modify m k f) k = ((Const.get? m k).map f).get! := + m.inductionOn fun _ => DHashMap.Const.get!_modify_self + +theorem getD_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {fallback : β} {f : β → β} : + Const.getD (Const.modify m k f) k' fallback = + if k == k' then + Const.get? m k |>.map f |>.getD fallback + else + Const.getD m k' fallback := + m.inductionOn fun _ => DHashMap.Const.getD_modify + +@[simp] +theorem getD_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} {f : β → β} : + Const.getD (Const.modify m k f) k fallback = ((Const.get? m k).map f).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_modify_self + +theorem getKey?_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} : + (Const.modify m k f).getKey? k' = + if k == k' then + if k ∈ m then some k else none + else + m.getKey? k' := + m.inductionOn fun _ => DHashMap.Const.getKey?_modify + +theorem getKey?_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + (Const.modify m k f).getKey? k = if k ∈ m then some k else none := + m.inductionOn fun _ => DHashMap.Const.getKey?_modify_self + +theorem getKey!_modify [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} {f : β → β} : + (Const.modify m k f).getKey! k' = + if k == k' then + if k ∈ m then k else default + else + m.getKey! k' := + m.inductionOn fun _ => DHashMap.Const.getKey!_modify + +theorem getKey!_modify_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} {f : β → β} : + (Const.modify m k f).getKey! k = if k ∈ m then k else default := + m.inductionOn fun _ => DHashMap.Const.getKey!_modify_self + +theorem getKey_modify [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} {f : β → β} + {h : k' ∈ Const.modify m k f} : + (Const.modify m k f).getKey k' h = + if k == k' then + k + else + haveI h' : k' ∈ m := mem_modify.mp h + m.getKey k' h' := + m.inductionOn (fun _ _ => DHashMap.Const.getKey_modify) h + +@[simp] +theorem getKey_modify_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} {f : β → β} + {h : k ∈ Const.modify m k f} : (Const.modify m k f).getKey k h = k := + m.inductionOn (fun _ _ => DHashMap.Const.getKey_modify_self) h + +theorem getKeyD_modify [EquivBEq α] [LawfulHashable α] {k k' fallback : α} {f : β → β} : + (Const.modify m k f).getKeyD k' fallback = + if k == k' then + if k ∈ m then k else fallback + else + m.getKeyD k' fallback := + m.inductionOn fun _ => DHashMap.Const.getKeyD_modify + +theorem getKeyD_modify_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k fallback : α} {f : β → β} : + (Const.modify m k f).getKeyD k fallback = if k ∈ m then k else fallback := + m.inductionOn fun _ => DHashMap.Const.getKeyD_modify_self + +end Const + +end Modify + +section Ext + +variable {m₁ m₂ : Std.ExtDHashMap α β} + +@[ext] +theorem ext_get? [LawfulBEq α] {m₁ m₂ : Std.ExtDHashMap α β} (h : ∀ k, m₁.get? k = m₂.get? k) : m₁ = m₂ := + m₁.inductionOn₂ m₂ (fun _ _ h => Quotient.sound (.of_forall_get?_eq h)) h + +namespace Const + +variable {β : Type v} {m₁ m₂ : ExtDHashMap α fun _ => β} + +theorem ext_getKey_get? [EquivBEq α] [LawfulHashable α] + (hk : ∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') + (hv : ∀ k, Const.get? m₁ k = Const.get? m₂ k) : m₁ = m₂ := + m₁.inductionOn₂ m₂ (fun _ _ hk hv => Quotient.sound + (.of_forall_getKey_eq_of_forall_constGet?_eq hk hv)) hk hv + +theorem ext_get? [LawfulBEq α] (h : ∀ k, get? m₁ k = get? m₂ k) : m₁ = m₂ := + m₁.inductionOn₂ m₂ (fun _ _ h => Quotient.sound (.of_forall_constGet?_eq h)) h + +theorem ext_getKey?_unit [EquivBEq α] [LawfulHashable α] {m₁ m₂ : ExtDHashMap α fun _ => Unit} + (h : ∀ k, m₁.getKey? k = m₂.getKey? k) : m₁ = m₂ := + m₁.inductionOn₂ m₂ (fun _ _ h => Quotient.sound (.of_forall_getKey?_unit_eq h)) h + +theorem ext_contains_unit [LawfulBEq α] {m₁ m₂ : ExtDHashMap α fun _ => Unit} + (h : ∀ k, m₁.contains k = m₂.contains k) : m₁ = m₂ := + m₁.inductionOn₂ m₂ (fun _ _ h => Quotient.sound (.of_forall_contains_unit_eq h)) h + +theorem ext_mem_unit [LawfulBEq α] {m₁ m₂ : ExtDHashMap α fun _ => Unit} + (h : ∀ k, k ∈ m₁ ↔ k ∈ m₂) : m₁ = m₂ := + m₁.inductionOn₂ m₂ (fun _ _ h => Quotient.sound (.of_forall_mem_unit_iff h)) h + +end Const + +end Ext + +section filterMap + +variable {γ : α → Type w} + +theorem filterMap_eq_empty_iff [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} : + m.filterMap f = ∅ ↔ ∀ k h, f k (m.get k h) = none := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.isEmpty_filterMap_iff + +theorem contains_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + (m.filterMap f).contains k = (m.get? k).any (f k · |>.isSome) := + m.inductionOn fun _ => DHashMap.contains_filterMap + +theorem mem_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + k ∈ m.filterMap f ↔ ∃ h, (f k (m.get k h)).isSome := + m.inductionOn fun _ => DHashMap.mem_filterMap + +theorem contains_of_contains_filterMap [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + (m.filterMap f).contains k = true → m.contains k = true := + m.inductionOn fun _ => DHashMap.contains_of_contains_filterMap + +theorem mem_of_mem_filterMap [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + k ∈ m.filterMap f → k ∈ m := + m.inductionOn fun _ => DHashMap.mem_of_mem_filterMap + +theorem size_filterMap_le_size [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → Option (γ a)} : + (m.filterMap f).size ≤ m.size := + m.inductionOn fun _ => DHashMap.size_filterMap_le_size + +theorem size_filterMap_eq_size_iff [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} : + (m.filterMap f).size = m.size ↔ ∀ (a : α) (h : a ∈ m), (f a (m.get a h)).isSome := + m.inductionOn fun _ => DHashMap.size_filterMap_eq_size_iff + +@[simp] +theorem get?_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + (m.filterMap f).get? k = (m.get? k).bind (f k) := + m.inductionOn fun _ => DHashMap.get?_filterMap + +theorem isSome_apply_of_mem_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + ∀ (h' : k ∈ m.filterMap f), + (f k (m.get k (mem_of_mem_filterMap h'))).isSome := + m.inductionOn fun _ => DHashMap.isSome_apply_of_mem_filterMap + +@[simp] +theorem get_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} {h'} : + (m.filterMap f).get k h' = + (f k (m.get k (mem_of_mem_filterMap h'))).get + (isSome_apply_of_mem_filterMap h') := + m.inductionOn (fun _ _ => DHashMap.get_filterMap) h' + +theorem get!_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} [Inhabited (γ k)] : + (m.filterMap f).get! k = ((m.get? k).bind (f k)).get! := + m.inductionOn fun _ => DHashMap.get!_filterMap + +theorem getD_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} {fallback : γ k} : + (m.filterMap f).getD k fallback = ((m.get? k).bind (f k)).getD fallback := + m.inductionOn fun _ => DHashMap.getD_filterMap + +theorem getKey?_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + (m.filterMap f).getKey? k = + (m.getKey? k).pfilter (fun x h' => + (f x (m.get x (mem_of_getKey?_eq_some h'))).isSome) := + m.inductionOn fun _ => DHashMap.getKey?_filterMap + +@[simp] +theorem getKey_filterMap [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → Option (γ a)} {k : α} {h'} : + (m.filterMap f).getKey k h' = m.getKey k (mem_of_mem_filterMap h') := + m.inductionOn (fun _ _ => DHashMap.getKey_filterMap) h' + +theorem getKey!_filterMap [LawfulBEq α] [Inhabited α] + {f : (a : α) → β a → Option (γ a)} {k : α} : + (m.filterMap f).getKey! k = + ((m.getKey? k).pfilter (fun x h' => + (f x (m.get x (mem_of_getKey?_eq_some h'))).isSome)).get! := + m.inductionOn fun _ => DHashMap.getKey!_filterMap + +theorem getKeyD_filterMap [LawfulBEq α] + {f : (a : α) → β a → Option (γ a)} {k fallback : α} : + (m.filterMap f).getKeyD k fallback = + ((m.getKey? k).pfilter (fun x h' => + (f x (m.get x (mem_of_getKey?_eq_some h'))).isSome)).getD fallback := + m.inductionOn fun _ => DHashMap.getKeyD_filterMap + +namespace Const + +variable {β : Type v} {γ : Type w} {m : ExtDHashMap α (fun _ => β)} + +theorem filterMap_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → β → Option γ} : + m.filterMap f = ∅ ↔ ∀ k h, f (m.getKey k h) (get m k h) = none := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.Const.isEmpty_filterMap_iff + +theorem mem_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + k ∈ m.filterMap f ↔ ∃ h, (f (m.getKey k h) (Const.get m k h)).isSome := + m.inductionOn fun _ => DHashMap.Const.mem_filterMap + +theorem size_filterMap_eq_size_iff [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} : + (m.filterMap f).size = m.size ↔ ∀ k h, (f (m.getKey k h) (Const.get m k h)).isSome := + m.inductionOn fun _ => DHashMap.Const.size_filterMap_eq_size_iff + +theorem get?_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + Const.get? (m.filterMap f) k = (Const.get? m k).pbind (fun x h' => + f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x) := + m.inductionOn fun _ => DHashMap.Const.get?_filterMap + +theorem get?_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k k' : α} (h : m.getKey? k = some k') : + Const.get? (m.filterMap f) k = (Const.get? m k).bind (f k') := + m.inductionOn (fun _ h => DHashMap.Const.get?_filterMap_of_getKey?_eq_some h) h + +theorem isSome_apply_of_mem_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + ∀ (h : k ∈ m.filterMap f), + (f (m.getKey k (mem_of_mem_filterMap h)) + (Const.get m k (mem_of_mem_filterMap h))).isSome := + m.inductionOn fun _ => DHashMap.Const.isSome_apply_of_mem_filterMap + +@[simp] +theorem get_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} {h} : + Const.get (m.filterMap f) k h = + (f (m.getKey k (mem_of_mem_filterMap h)) + (Const.get m k (mem_of_mem_filterMap h))).get + (isSome_apply_of_mem_filterMap h) := + m.inductionOn (fun _ _ => DHashMap.Const.get_filterMap) h + +theorem get!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → Option γ} {k : α} : + Const.get! (m.filterMap f) k = + ((Const.get? m k).pbind (fun x h' => + f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).get! := + m.inductionOn fun _ => DHashMap.Const.get!_filterMap + +theorem get!_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → Option γ} {k k' : α} (h : m.getKey? k = some k') : + Const.get! (m.filterMap f) k = ((Const.get? m k).bind (f k')).get! := + m.inductionOn (fun _ h => DHashMap.Const.get!_filterMap_of_getKey?_eq_some h) h + +theorem getD_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} {fallback : γ} : + Const.getD (m.filterMap f) k fallback = + ((Const.get? m k).pbind (fun x h' => + f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_filterMap + +theorem getD_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k k' : α} {fallback : γ} (h : m.getKey? k = some k') : + Const.getD (m.filterMap f) k fallback = ((Const.get? m k).bind (f k')).getD fallback := + m.inductionOn (fun _ h => DHashMap.Const.getD_filterMap_of_getKey?_eq_some h) h + +theorem getKey?_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + (m.filterMap f).getKey? k = + (m.getKey? k).pfilter (fun x h' => + (f x (Const.get m x (mem_of_getKey?_eq_some h'))).isSome) := + m.inductionOn fun _ => DHashMap.Const.getKey?_filterMap + +theorem getKey!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → β → Option γ} {k : α} : + (m.filterMap f).getKey! k = + ((m.getKey? k).pfilter (fun x h' => + (f x (Const.get m x (mem_of_getKey?_eq_some h'))).isSome)).get! := + m.inductionOn fun _ => DHashMap.Const.getKey!_filterMap + +theorem getKeyD_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k fallback : α} : + (m.filterMap f).getKeyD k fallback = + ((m.getKey? k).pfilter (fun x h' => + (f x (Const.get m x (mem_of_getKey?_eq_some h'))).isSome)).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getKeyD_filterMap + +end Const + +end filterMap + +section filter + +theorem filterMap_eq_filter [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → Bool} : + m.filterMap (fun k => Option.guard (fun v => f k v)) = m.filter f := + m.inductionOn fun _ => Quotient.sound DHashMap.filterMap_equiv_filter + +@[simp] +theorem filter_eq_empty_iff [LawfulBEq α] + {f : (a : α) → β a → Bool} : + m.filter f = ∅ ↔ ∀ k h, f k (m.get k h) = false := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.isEmpty_filter_iff + +theorem filter_key_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → Bool} : + m.filter (fun a _ => f a) = ∅ ↔ ∀ k h, f (m.getKey k h) = false := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.isEmpty_filter_key_iff + +theorem contains_filter [LawfulBEq α] {f : (a : α) → β a → Bool} {k : α} : + (m.filter f).contains k = (m.get? k).any (f k) := + m.inductionOn fun _ => DHashMap.contains_filter + +theorem mem_filter [LawfulBEq α] {f : (a : α) → β a → Bool} {k : α} : + k ∈ m.filter f ↔ ∃ h, f k (m.get k h) := + m.inductionOn fun _ => DHashMap.mem_filter + +theorem mem_filter_key [EquivBEq α] [LawfulHashable α] {f : α → Bool} {k : α} : + k ∈ m.filter (fun a _ => f a) ↔ ∃ h, f (m.getKey k h) := + m.inductionOn fun _ => DHashMap.mem_filter_key + +theorem contains_of_contains_filter [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → Bool} {k : α} : + (m.filter f).contains k = true → m.contains k = true := + m.inductionOn fun _ => DHashMap.contains_of_contains_filter + +theorem mem_of_mem_filter [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → Bool} {k : α} : + k ∈ (m.filter f) → k ∈ m := + m.inductionOn fun _ => DHashMap.mem_of_mem_filter + +theorem size_filter_le_size [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → Bool} : + (m.filter f).size ≤ m.size := + m.inductionOn fun _ => DHashMap.size_filter_le_size + +theorem size_filter_eq_size_iff [LawfulBEq α] {f : (a : α) → β a → Bool} : + (m.filter f).size = m.size ↔ ∀ k h, f k (m.get k h) := + m.inductionOn fun _ => DHashMap.size_filter_eq_size_iff + +theorem filter_eq_self_iff [LawfulBEq α] {f : (a : α) → β a → Bool} : + m.filter f = m ↔ ∀ k h, f k (m.get k h) := + m.inductionOn fun _ => Iff.trans ⟨Quotient.exact, Quotient.sound⟩ DHashMap.filter_equiv_self_iff + +theorem filter_key_equiv_self_iff [EquivBEq α] [LawfulHashable α] {f : (a : α) → Bool} : + m.filter (fun k _ => f k) = m ↔ ∀ k h, f (m.getKey k h) := + m.inductionOn fun _ => Iff.trans ⟨Quotient.exact, Quotient.sound⟩ DHashMap.filter_key_equiv_self_iff + +theorem size_filter_key_eq_size_iff [EquivBEq α] [LawfulHashable α] {f : α → Bool} : + (m.filter fun k _ => f k).size = m.size ↔ ∀ k h, f (m.getKey k h) := + m.inductionOn fun _ => DHashMap.size_filter_key_eq_size_iff + +@[simp] +theorem get?_filter [LawfulBEq α] + {f : (a : α) → β a → Bool} {k : α} : + (m.filter f).get? k = (m.get? k).filter (f k) := + m.inductionOn fun _ => DHashMap.get?_filter + +@[simp] +theorem get_filter [LawfulBEq α] + {f : (a : α) → β a → Bool} {k : α} {h'} : + (m.filter f).get k h' = m.get k (mem_of_mem_filter h') := + m.inductionOn (fun _ _ => DHashMap.get_filter) h' + +theorem get!_filter [LawfulBEq α] + {f : (a : α) → β a → Bool} {k : α} [Inhabited (β k)] : + (m.filter f).get! k = ((m.get? k).filter (f k)).get! := + m.inductionOn fun _ => DHashMap.get!_filter + +theorem getD_filter [LawfulBEq α] + {f : (a : α) → β a → Bool} {k : α} {fallback : β k} : + (m.filter f).getD k fallback = ((m.get? k).filter (f k)).getD fallback := + m.inductionOn fun _ => DHashMap.getD_filter + +theorem getKey?_filter [LawfulBEq α] + {f : (a : α) → β a → Bool} {k : α} : + (m.filter f).getKey? k = + (m.getKey? k).pfilter (fun x h' => + f x (m.get x (mem_of_getKey?_eq_some h'))) := + m.inductionOn fun _ => DHashMap.getKey?_filter + +theorem getKey?_filter_key [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} : + (m.filter fun k _ => f k).getKey? k = (m.getKey? k).filter f := + m.inductionOn fun _ => DHashMap.getKey?_filter_key + +@[simp] +theorem getKey_filter [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → Bool} {k : α} {h'} : + (m.filter f).getKey k h' = m.getKey k (mem_of_mem_filter h') := + m.inductionOn (fun _ _ => DHashMap.getKey_filter) h' + +theorem getKey!_filter [LawfulBEq α] [Inhabited α] + {f : (a : α) → β a → Bool} {k : α} : + (m.filter f).getKey! k = + ((m.getKey? k).pfilter (fun x h' => + f x (m.get x (mem_of_getKey?_eq_some h')))).get! := + m.inductionOn fun _ => DHashMap.getKey!_filter + +theorem getKey!_filter_key [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → Bool} {k : α} : + (m.filter fun k _ => f k).getKey! k = ((m.getKey? k).filter f).get! := + m.inductionOn fun _ => DHashMap.getKey!_filter_key + +theorem getKeyD_filter [LawfulBEq α] + {f : (a : α) → β a → Bool} {k fallback : α} : + (m.filter f).getKeyD k fallback = + ((m.getKey? k).pfilter (fun x h' => + f x (m.get x (mem_of_getKey?_eq_some h')))).getD fallback := + m.inductionOn fun _ => DHashMap.getKeyD_filter + +theorem getKeyD_filter_key [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k fallback : α} : + (m.filter fun k _ => f k).getKeyD k fallback = ((m.getKey? k).filter f).getD fallback := + m.inductionOn fun _ => DHashMap.getKeyD_filter_key + +namespace Const + +variable {β : Type v} {γ : Type w} {m : ExtDHashMap α (fun _ => β)} + +theorem filter_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → β → Bool} : + m.filter f = ∅ ↔ ∀ k h, f (m.getKey k h) (Const.get m k h) = false := + isEmpty_iff.symm.trans <| m.inductionOn fun _ => DHashMap.Const.isEmpty_filter_iff + +theorem mem_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + k ∈ m.filter f ↔ ∃ (h' : k ∈ m), + f (m.getKey k h') (Const.get m k h') := + m.inductionOn fun _ => DHashMap.Const.mem_filter + +theorem size_filter_le_size [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} : + (m.filter f).size ≤ m.size := + m.inductionOn fun _ => DHashMap.Const.size_filter_le_size + +theorem size_filter_eq_size_iff [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} : + (m.filter f).size = m.size ↔ ∀ (a : α) (h : a ∈ m), + f (m.getKey a h) (Const.get m a h) := + m.inductionOn fun _ => DHashMap.Const.size_filter_eq_size_iff + +theorem filter_eq_self_iff [EquivBEq α] [LawfulHashable α] {f : α → β → Bool} : + m.filter f = m ↔ ∀ k h, f (m.getKey k h) (Const.get m k h) := + m.inductionOn fun _ => Iff.trans ⟨Quotient.exact, Quotient.sound⟩ DHashMap.Const.filter_equiv_self_iff + +theorem get?_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + Const.get? (m.filter f) k = (Const.get? m k).pfilter (fun x h' => + f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x) := + m.inductionOn fun _ => DHashMap.Const.get?_filter + +theorem get?_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k k' : α} : + m.getKey? k = some k' → + Const.get? (m.filter f) k = (Const.get? m k).filter (fun x => f k' x) := + m.inductionOn fun _ => DHashMap.Const.get?_filter_of_getKey?_eq_some + +@[simp] +theorem get_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} {h'} : + Const.get (m.filter f) k h' = Const.get m k (mem_of_mem_filter h') := + m.inductionOn (fun _ _ => DHashMap.Const.get_filter) h' + +theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β] + {f : α → β → Bool} {k : α} : + Const.get! (m.filter f) k = + ((Const.get? m k).pfilter (fun x h' => + f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).get! := + m.inductionOn fun _ => DHashMap.Const.get!_filter + +theorem get!_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited β] + {f : α → β → Bool} {k k' : α} : + m.getKey? k = some k' → + Const.get! (m.filter f) k = ((Const.get? m k).filter (fun x => f k' x)).get! := + m.inductionOn fun _ => DHashMap.Const.get!_filter_of_getKey?_eq_some + +theorem getD_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} {fallback : β} : + Const.getD (m.filter f) k fallback = ((Const.get? m k).pfilter (fun x h' => + f (m.getKey k (mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_filter + +theorem getD_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k k' : α} {fallback : β} : + m.getKey? k = some k' → + Const.getD (m.filter f) k fallback = + ((Const.get? m k).filter (fun x => f k' x)).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_filter_of_getKey?_eq_some + +theorem getKey?_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + (m.filter f).getKey? k = + (m.getKey? k).pfilter (fun x h' => + (f x (Const.get m x (mem_of_getKey?_eq_some h')))) := + m.inductionOn fun _ => DHashMap.Const.getKey?_filter + +theorem getKey!_filter [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → β → Bool} {k : α} : + (m.filter f).getKey! k = + ((m.getKey? k).pfilter (fun x h' => + (f x (Const.get m x (mem_of_getKey?_eq_some h'))))).get! := + m.inductionOn fun _ => DHashMap.Const.getKey!_filter + +theorem getKeyD_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k fallback : α} : + (m.filter f).getKeyD k fallback = + ((m.getKey? k).pfilter (fun x h' => + (f x (Const.get m x (mem_of_getKey?_eq_some h'))))).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getKeyD_filter + +end Const + +end filter + +section map + +variable {γ : α → Type w} {δ : α → Type w'} + +@[simp] +theorem map_id_fun [EquivBEq α] [LawfulHashable α] : m.map (fun _ v => v) = m := + m.inductionOn fun _ => Quotient.sound DHashMap.map_id_equiv + +@[simp] +theorem map_map [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → γ a} {g : (a : α) → γ a → δ a} : + (m.map f).map g = m.map fun k v => g k (f k v) := + m.inductionOn fun _ => Quotient.sound DHashMap.map_map_equiv + +theorem filterMap_eq_map [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → γ a} : + (m.filterMap (fun k v => some (f k v))) = m.map f := + m.inductionOn fun _ => Quotient.sound DHashMap.filterMap_equiv_map + +@[simp] +theorem map_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : (a : α) → β a → γ a} : + m.map f = ∅ ↔ m = ∅ := by + simp only [← isEmpty_iff, Bool.coe_iff_coe] + exact m.inductionOn fun _ => DHashMap.isEmpty_map + +theorem contains_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k : α} : + (m.map f).contains k = m.contains k := + m.inductionOn fun _ => DHashMap.contains_map + +theorem contains_of_contains_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k : α} : + (m.map f).contains k = true → m.contains k = true := + m.inductionOn fun _ => DHashMap.contains_of_contains_map + +@[simp] +theorem mem_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k : α} : + k ∈ m.map f ↔ k ∈ m := + m.inductionOn fun _ => DHashMap.mem_map + +theorem mem_of_mem_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k : α} : + k ∈ m.map f → k ∈ m := + m.inductionOn fun _ => DHashMap.mem_of_mem_map + +@[simp] +theorem size_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} : + (m.map f).size = m.size := + m.inductionOn fun _ => DHashMap.size_map + +@[simp] +theorem get?_map [LawfulBEq α] + {f : (a : α) → β a → γ a} {k : α} : + (m.map f).get? k = (m.get? k).map (f k) := + m.inductionOn fun _ => DHashMap.get?_map + +@[simp] +theorem get_map [LawfulBEq α] + {f : (a : α) → β a → γ a} {k : α} {h'} : + (m.map f).get k h' = f k (m.get k (mem_of_mem_map h')) := + m.inductionOn (fun _ _ => DHashMap.get_map) h' + +theorem get!_map [LawfulBEq α] + {f : (a : α) → β a → γ a} {k : α} [Inhabited (γ k)] : + (m.map f).get! k = ((m.get? k).map (f k)).get! := + m.inductionOn fun _ => DHashMap.get!_map + +theorem getD_map [LawfulBEq α] + {f : (a : α) → β a → γ a} {k : α} {fallback : γ k} : + (m.map f).getD k fallback = ((m.get? k).map (f k)).getD fallback := + m.inductionOn fun _ => DHashMap.getD_map + +@[simp] +theorem getKey?_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k : α} : + (m.map f).getKey? k = m.getKey? k := + m.inductionOn fun _ => DHashMap.getKey?_map + +@[simp] +theorem getKey_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k : α} {h'} : + (m.map f).getKey k h' = m.getKey k (mem_of_mem_map h') := + m.inductionOn (fun _ _ => DHashMap.getKey_map) h' + +@[simp] +theorem getKey!_map [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : (a : α) → β a → γ a} {k : α} : + (m.map f).getKey! k = m.getKey! k := + m.inductionOn fun _ => DHashMap.getKey!_map + +@[simp] +theorem getKeyD_map [EquivBEq α] [LawfulHashable α] + {f : (a : α) → β a → γ a} {k fallback : α} : + (m.map f).getKeyD k fallback = m.getKeyD k fallback := + m.inductionOn fun _ => DHashMap.getKeyD_map + +namespace Const + +variable {β : Type v} {γ : Type w} {m : ExtDHashMap α fun _ => β} + +theorem get?_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + Const.get? (m.map f) k = (Const.get? m k).pmap (fun v h' => f (m.getKey k h') v) + (fun _ h' => mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h')) := + m.inductionOn fun _ => DHashMap.Const.get?_map + +theorem get?_map_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k k' : α} (h : m.getKey? k = some k') : + Const.get? (m.map f) k = (Const.get? m k).map (f k') := + m.inductionOn (fun _ h => DHashMap.Const.get?_map_of_getKey?_eq_some h) h + +@[simp] +theorem get_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} {h'} : + Const.get (m.map f) k h' = + f (m.getKey k (mem_of_mem_map h')) (Const.get m k (mem_of_mem_map h')) := + m.inductionOn (fun _ _ => DHashMap.Const.get_map) h' + +theorem get!_map [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → γ} {k : α} : + Const.get! (m.map f) k = + ((get? m k).pmap (fun v h => f (m.getKey k h) v) + (fun _ h' => mem_iff_isSome_get?.mpr (Option.isSome_of_mem h'))).get! := + m.inductionOn fun _ => DHashMap.Const.get!_map + +theorem get!_map_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → γ} {k k' : α} (h : m.getKey? k = some k') : + Const.get! (m.map f) k = ((Const.get? m k).map (f k')).get! := + m.inductionOn (fun _ h => DHashMap.Const.get!_map_of_getKey?_eq_some h) h + +theorem getD_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} {fallback : γ} : + Const.getD (m.map f) k fallback = + ((get? m k).pmap (fun v h => f (m.getKey k h) v) + (fun _ h' => mem_iff_isSome_get?.mpr (Option.isSome_of_eq_some h'))).getD fallback := + m.inductionOn fun _ => DHashMap.Const.getD_map + +theorem getD_map_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → γ} {k k' : α} {fallback : γ} (h : m.getKey? k = some k') : + Const.getD (m.map f) k fallback = ((Const.get? m k).map (f k')).getD fallback := + m.inductionOn (fun _ h => DHashMap.Const.getD_map_of_getKey?_eq_some h) h + +end Const + +end map + +end Std.ExtDHashMap diff --git a/src/Std/Data/ExtHashMap.lean b/src/Std/Data/ExtHashMap.lean new file mode 100644 index 0000000000..7bc2eaf27f --- /dev/null +++ b/src/Std/Data/ExtHashMap.lean @@ -0,0 +1,8 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtHashMap.Basic +import Std.Data.ExtHashMap.Lemmas diff --git a/src/Std/Data/ExtHashMap/Basic.lean b/src/Std/Data/ExtHashMap/Basic.lean new file mode 100644 index 0000000000..f3385bb1f6 --- /dev/null +++ b/src/Std/Data/ExtHashMap/Basic.lean @@ -0,0 +1,248 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtDHashMap.Basic + +set_option linter.missingDocs true +set_option autoImplicit false + +/-! +# Extensional hash maps + +This module develops the type `Std.ExtHashMap` of extensional hash maps. Dependent hash maps +are defined in `Std.Data.ExtDHashMap`. + +Lemmas about the operations on `Std.ExtHashMap` are available in the +module `Std.Data.ExtHashMap.Lemmas`. +-/ + +universe u v w + +variable {α : Type u} {β : Type v} {γ : Type w} {_ : BEq α} {_ : Hashable α} + +namespace Std + +/-- +Hash maps. + +This is a simple separate-chaining hash table. The data of the hash map consists of a cached size +and an array of buckets, where each bucket is a linked list of key-value pais. The number of buckets +is always a power of two. The hash map doubles its size upon inserting an element such that the +number of elements is more than 75% of the number of buckets. + +The hash table is backed by an `Array`. Users should make sure that the hash map is used linearly to +avoid expensive copies. + +The hash map uses `==` (provided by the `BEq` typeclass) to compare keys and `hash` (provided by +the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==` +should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the +`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq +instance is lawful, i.e., if `a == b` implies `a = b`. + +In contrast to regular hash maps, `Std.ExtHashMap` offers several extensionality lemmas +and therefore has more lemmas about equality of hash maps. This however also makes it lose the +ability to iterate freely over hash maps. + +These hash maps contain a bundled well-formedness invariant, which means that they cannot +be used in nested inductive types. For these use cases, `Std.HashMap.Raw` and +`Std.HashMap.Raw.WF` unbundle the invariant from the hash map. When in doubt, prefer +`HashMap` or `ExtHashMap` over `HashMap.Raw`. + +Dependent hash maps, in which keys may occur in their values' types, are available as +`Std.ExtDHashMap` in the module `Std.Data.ExtDHashMap`. +-/ +structure ExtHashMap (α : Type u) (β : Type v) [BEq α] [Hashable α] where + /-- Internal implementation detail of the hash map -/ + inner : ExtDHashMap α (fun _ => β) + +namespace ExtHashMap + +@[inline, inherit_doc ExtDHashMap.emptyWithCapacity] +def emptyWithCapacity [BEq α] [Hashable α] (capacity := 8) : + ExtHashMap α β := + ⟨ExtDHashMap.emptyWithCapacity capacity⟩ + +instance [BEq α] [Hashable α] : EmptyCollection (ExtHashMap α β) where + emptyCollection := emptyWithCapacity + +instance [BEq α] [Hashable α] : Inhabited (ExtHashMap α β) where + default := ∅ + +@[inline, inherit_doc ExtDHashMap.insert] +def insert [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) + (b : β) : ExtHashMap α β := + ⟨m.inner.insert a b⟩ + +instance [EquivBEq α] [LawfulHashable α] : Singleton (α × β) (ExtHashMap α β) where + singleton | ⟨a, b⟩ => (∅ : ExtHashMap α β).insert a b + +instance [EquivBEq α] [LawfulHashable α] : Insert (α × β) (ExtHashMap α β) where + insert | ⟨a, b⟩, s => s.insert a b + +instance [EquivBEq α] [LawfulHashable α] : LawfulSingleton (α × β) (ExtHashMap α β) := + ⟨fun _ => rfl⟩ + +@[inline, inherit_doc ExtDHashMap.insertIfNew] +def insertIfNew [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) + (a : α) (b : β) : ExtHashMap α β := + ⟨m.inner.insertIfNew a b⟩ + +@[inline, inherit_doc ExtDHashMap.containsThenInsert] +def containsThenInsert [EquivBEq α] [LawfulHashable α] + (m : ExtHashMap α β) (a : α) (b : β) : Bool × ExtHashMap α β := + let ⟨replaced, r⟩ := m.inner.containsThenInsert a b + ⟨replaced, ⟨r⟩⟩ + +@[inline, inherit_doc ExtDHashMap.containsThenInsertIfNew] +def containsThenInsertIfNew [EquivBEq α] [LawfulHashable α] + (m : ExtHashMap α β) (a : α) (b : β) : Bool × ExtHashMap α β := + let ⟨replaced, r⟩ := m.inner.containsThenInsertIfNew a b + ⟨replaced, ⟨r⟩⟩ + +/-- +Checks whether a key is present in a map, returning the associate value, and inserts a value for +the key if it was not found. + +If the returned value is `some v`, then the returned map is unaltered. If it is `none`, then the +returned map has a new value inserted. + +Equivalent to (but potentially faster than) calling `get?` followed by `insertIfNew`. +-/ +@[inline] +def getThenInsertIfNew? [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) (b : β) : + Option β × ExtHashMap α β := + let ⟨previous, r⟩ := ExtDHashMap.Const.getThenInsertIfNew? m.inner a b + ⟨previous, ⟨r⟩⟩ + +/-- +The notation `m[a]?` is preferred over calling this function directly. + +Tries to retrieve the mapping for the given key, returning `none` if no such mapping is present. +-/ +@[inline] def get? [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) : Option β := + ExtDHashMap.Const.get? m.inner a + +@[inline, inherit_doc ExtDHashMap.contains] +def contains [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) + (a : α) : Bool := + m.inner.contains a + +instance [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] : Membership α (ExtHashMap α β) where + mem m a := a ∈ m.inner + +instance [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] + {m : ExtHashMap α β} {a : α} : Decidable (a ∈ m) := + inferInstanceAs (Decidable (a ∈ m.inner)) + +/-- +The notation `m[a]` or `m[a]'h` is preferred over calling this function directly. + +Retrieves the mapping for the given key. Ensures that such a mapping exists by requiring a proof of +`a ∈ m`. +-/ +@[inline] def get [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) (h : a ∈ m) : β := + ExtDHashMap.Const.get m.inner a h + +@[inline, inherit_doc ExtDHashMap.Const.getD] +def getD [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) + (fallback : β) : β := + ExtDHashMap.Const.getD m.inner a fallback + +/-- +The notation `m[a]!` is preferred over calling this function directly. + +Tries to retrieve the mapping for the given key, panicking if no such mapping is present. +-/ +@[inline] +def get! [EquivBEq α] [LawfulHashable α] [Inhabited β] (m : ExtHashMap α β) (a : α) : β := + ExtDHashMap.Const.get! m.inner a + +instance [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] : + GetElem? (ExtHashMap α β) α β (fun m a => a ∈ m) where + getElem m a h := m.get a h + getElem? m a := m.get? a + getElem! m a := m.get! a + +@[inline, inherit_doc ExtDHashMap.getKey?] +def getKey? [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) : Option α := + ExtDHashMap.getKey? m.inner a + +@[inline, inherit_doc ExtDHashMap.getKey] +def getKey [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) (h : a ∈ m) : α := + ExtDHashMap.getKey m.inner a h + +@[inline, inherit_doc ExtDHashMap.getKeyD] +def getKeyD [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) (fallback : α) : α := + ExtDHashMap.getKeyD m.inner a fallback + +@[inline, inherit_doc ExtDHashMap.getKey!] +def getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] (m : ExtHashMap α β) (a : α) : α := + ExtDHashMap.getKey! m.inner a + +@[inline, inherit_doc ExtDHashMap.erase] +def erase [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) : + ExtHashMap α β := + ⟨m.inner.erase a⟩ + +@[inline, inherit_doc ExtDHashMap.size] +def size [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) : Nat := + m.inner.size + +@[inline, inherit_doc ExtDHashMap.isEmpty] +def isEmpty [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) : Bool := + m.inner.isEmpty + +@[inline, inherit_doc ExtDHashMap.Const.ofList] +def ofList [BEq α] [Hashable α] (l : List (α × β)) : + ExtHashMap α β := + ⟨ExtDHashMap.Const.ofList l⟩ + +@[inline, inherit_doc ExtDHashMap.Const.unitOfList] +def unitOfList [BEq α] [Hashable α] (l : List α) : + ExtHashMap α Unit := + ⟨ExtDHashMap.Const.unitOfList l⟩ + +@[inline, inherit_doc ExtDHashMap.filter] +def filter [EquivBEq α] [LawfulHashable α] (f : α → β → Bool) + (m : ExtHashMap α β) : ExtHashMap α β := + ⟨m.inner.filter f⟩ + +@[inline, inherit_doc ExtDHashMap.map] +def map [EquivBEq α] [LawfulHashable α] (f : α → β → γ) + (m : ExtHashMap α β) : ExtHashMap α γ := + ⟨m.inner.map f⟩ + +@[inline, inherit_doc ExtDHashMap.filterMap] +def filterMap [EquivBEq α] [LawfulHashable α] (f : α → β → Option γ) + (m : ExtHashMap α β) : ExtHashMap α γ := + ⟨m.inner.filterMap f⟩ + +@[inline, inherit_doc ExtDHashMap.modify] +def modify [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) (f : β → β) : + ExtHashMap α β := + ⟨ExtDHashMap.Const.modify m.inner a f⟩ + +@[inline, inherit_doc ExtDHashMap.alter] +def alter [EquivBEq α] [LawfulHashable α] (m : ExtHashMap α β) (a : α) + (f : Option β → Option β) : ExtHashMap α β := + ⟨ExtDHashMap.Const.alter m.inner a f⟩ + +@[inline, inherit_doc ExtDHashMap.Const.insertMany] +def insertMany [EquivBEq α] [LawfulHashable α] {ρ : Type w} + [ForIn Id ρ (α × β)] (m : ExtHashMap α β) (l : ρ) : ExtHashMap α β := + ⟨ExtDHashMap.Const.insertMany m.inner l⟩ + +@[inline, inherit_doc ExtDHashMap.Const.insertManyIfNewUnit] +def insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {ρ : Type w} [ForIn Id ρ α] (m : ExtHashMap α Unit) (l : ρ) : ExtHashMap α Unit := + ⟨ExtDHashMap.Const.insertManyIfNewUnit m.inner l⟩ + +@[inline, inherit_doc ExtDHashMap.Const.unitOfArray] +def unitOfArray [BEq α] [Hashable α] (l : Array α) : + ExtHashMap α Unit := + ⟨ExtDHashMap.Const.unitOfArray l⟩ + +end Std.ExtHashMap diff --git a/src/Std/Data/ExtHashMap/Lemmas.lean b/src/Std/Data/ExtHashMap/Lemmas.lean new file mode 100644 index 0000000000..ff086e624f --- /dev/null +++ b/src/Std/Data/ExtHashMap/Lemmas.lean @@ -0,0 +1,2116 @@ +/- +Copyright (c) 2025 Robin Arnez, LLC. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtHashMap.Basic +import Std.Data.ExtDHashMap.Lemmas + +/-! +# Extensional hash map lemmas + +This module contains lemmas about `Std.ExtHashMap`. +-/ + +set_option linter.missingDocs true +set_option autoImplicit false + +universe u v w w' + +variable {α : Type u} {β : Type v} {γ : Type w} {δ : Type w'} {_ : BEq α} {_ : Hashable α} + +namespace Std.ExtHashMap + +section + +variable {m : ExtHashMap α β} + +private theorem ext {m m' : ExtHashMap α β} : m.inner = m'.inner → m = m' := by + cases m; cases m'; rintro rfl; rfl + +private theorem ext_iff {m m' : ExtHashMap α β} : m = m' ↔ m.inner = m'.inner := + ⟨fun h => h ▸ rfl, ext⟩ + +@[simp] +theorem isEmpty_iff [EquivBEq α] [LawfulHashable α] : m.isEmpty ↔ m = ∅ := + ExtDHashMap.isEmpty_iff.trans ext_iff.symm + +@[simp] +theorem isEmpty_eq_false_iff [EquivBEq α] [LawfulHashable α] : m.isEmpty = false ↔ ¬m = ∅ := + (Bool.not_eq_true _).symm.to_iff.trans (not_congr isEmpty_iff) + +@[simp] +theorem empty_eq : ∅ = m ↔ m = ∅ := eq_comm + +@[simp] +theorem emptyWithCapacity_eq [EquivBEq α] [LawfulHashable α] {c} : (emptyWithCapacity c : ExtHashMap α β) = ∅ := + ext ExtDHashMap.emptyWithCapacity_eq + +@[simp] +theorem not_insert_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + ¬m.insert k v = ∅ := + (not_congr ext_iff).mpr ExtDHashMap.not_insert_eq_empty + +theorem mem_iff_contains [EquivBEq α] [LawfulHashable α] {a : α} : a ∈ m ↔ m.contains a := + ExtDHashMap.mem_iff_contains + +theorem contains_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : + m.contains a = m.contains b := + ExtDHashMap.contains_congr hab + +theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : + a ∈ m ↔ b ∈ m := + ExtDHashMap.mem_congr hab + +@[simp] theorem contains_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : ExtHashMap α β).contains a = false := + ExtDHashMap.contains_empty + +@[simp] theorem not_mem_empty [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ (∅ : ExtHashMap α β) := + ExtDHashMap.not_mem_empty + +theorem eq_empty_iff_forall_contains [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ ∀ a, m.contains a = false := + ext_iff.trans ExtDHashMap.eq_empty_iff_forall_contains + +theorem eq_empty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ ∀ a, ¬a ∈ m := + ext_iff.trans ExtDHashMap.eq_empty_iff_forall_not_mem + +@[simp] theorem insert_eq_insert [EquivBEq α] [LawfulHashable α] {p : α × β} : + Insert.insert p m = m.insert p.1 p.2 := + rfl + +@[simp] theorem singleton_eq_insert [EquivBEq α] [LawfulHashable α] {p : α × β} : + Singleton.singleton p = (∅ : ExtHashMap α β).insert p.1 p.2 := + rfl + +@[simp] +theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insert k v).contains a = (k == a || m.contains a) := + ExtDHashMap.contains_insert + +@[simp] +theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + a ∈ m.insert k v ↔ k == a ∨ a ∈ m := + ExtDHashMap.mem_insert + +theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insert k v).contains a → (k == a) = false → m.contains a := + ExtDHashMap.contains_of_contains_insert + +theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + a ∈ m.insert k v → (k == a) = false → a ∈ m := + ExtDHashMap.mem_of_mem_insert + +theorem contains_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v).contains k := by simp + +theorem mem_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : k ∈ m.insert k v := by + simp + +@[simp] +theorem size_empty [EquivBEq α] [LawfulHashable α] : (∅ : ExtHashMap α β).size = 0 := + ExtDHashMap.size_empty + +theorem eq_empty_iff_size_eq_zero [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ m.size = 0 := + ext_iff.trans ExtDHashMap.eq_empty_iff_size_eq_zero + +theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v).size = if k ∈ m then m.size else m.size + 1 := + ExtDHashMap.size_insert + +theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + m.size ≤ (m.insert k v).size := + ExtDHashMap.size_le_size_insert + +theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v).size ≤ m.size + 1 := + ExtDHashMap.size_insert_le + +@[simp] +theorem erase_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : ExtHashMap α β).erase a = ∅ := + ext ExtDHashMap.erase_empty + +@[simp] +theorem erase_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} : + m.erase k = ∅ ↔ m = ∅ ∨ m.size = 1 ∧ k ∈ m := by + simpa only [ext_iff] using ExtDHashMap.erase_eq_empty_iff + +@[simp] +theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).contains a = (!(k == a) && m.contains a) := + ExtDHashMap.contains_erase + +@[simp] +theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + a ∈ m.erase k ↔ (k == a) = false ∧ a ∈ m := + ExtDHashMap.mem_erase + +theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).contains a → m.contains a := + ExtDHashMap.contains_of_contains_erase + +theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.erase k → a ∈ m := + ExtDHashMap.mem_of_mem_erase + +theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} : + (m.erase k).size = if k ∈ m then m.size - 1 else m.size := + ExtDHashMap.size_erase + +theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size := + ExtDHashMap.size_erase_le + +theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} : + m.size ≤ (m.erase k).size + 1 := + ExtDHashMap.size_le_size_erase + +@[simp] +theorem containsThenInsert_fst [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.containsThenInsert k v).1 = m.contains k := + ExtDHashMap.containsThenInsert_fst + +@[simp] +theorem containsThenInsert_snd [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.containsThenInsert k v).2 = m.insert k v := + ext (ExtDHashMap.containsThenInsert_snd) + +@[simp] +theorem containsThenInsertIfNew_fst [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.containsThenInsertIfNew k v).1 = m.contains k := + ExtDHashMap.containsThenInsertIfNew_fst + +@[simp] +theorem containsThenInsertIfNew_snd [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.containsThenInsertIfNew k v).2 = m.insertIfNew k v := + ext ExtDHashMap.containsThenInsertIfNew_snd + +@[simp] theorem get_eq_getElem [EquivBEq α] [LawfulHashable α] {a : α} {h} : get m a h = m[a]'h := rfl +@[simp] theorem get?_eq_getElem? [EquivBEq α] [LawfulHashable α] {a : α} : get? m a = m[a]? := rfl +@[simp] theorem get!_eq_getElem! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : get! m a = m[a]! := rfl + +@[simp] +theorem getElem?_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : ExtHashMap α β)[a]? = none := + ExtDHashMap.Const.get?_empty + +theorem getElem?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insert k v)[a]? = if k == a then some v else m[a]? := + ExtDHashMap.Const.get?_insert + +@[simp] +theorem getElem?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v)[k]? = some v := + ExtDHashMap.Const.get?_insert_self + +theorem contains_eq_isSome_getElem? [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = m[a]?.isSome := + ExtDHashMap.Const.contains_eq_isSome_get? + +theorem mem_iff_isSome_getElem? [EquivBEq α] [LawfulHashable α] {a : α} : + a ∈ m ↔ m[a]?.isSome := + ExtDHashMap.Const.mem_iff_isSome_get? + +theorem getElem?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = false → m[a]? = none := + ExtDHashMap.Const.get?_eq_none_of_contains_eq_false + +theorem getElem?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m → m[a]? = none := + ExtDHashMap.Const.get?_eq_none + +theorem getElem?_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k)[a]? = if k == a then none else m[a]? := + ExtDHashMap.Const.get?_erase + +@[simp] +theorem getElem?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k)[k]? = none := + ExtDHashMap.Const.get?_erase_self + +theorem getElem?_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : m[a]? = m[b]? := + ExtDHashMap.Const.get?_congr hab + +theorem getElem_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} : + (m.insert k v)[a]'h₁ = + if h₂ : k == a then v else m[a]'(mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) := + ExtDHashMap.Const.get_insert (h₁ := h₁) + +@[simp] +theorem getElem_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v)[k]'mem_insert_self = v := + ExtDHashMap.Const.get_insert_self + +@[simp] +theorem getElem_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} : + (m.erase k)[a]'h' = m[a]'(mem_of_mem_erase h') := + ExtDHashMap.Const.get_erase (h' := h') + +theorem getElem?_eq_some_getElem [EquivBEq α] [LawfulHashable α] {a : α} (h' : a ∈ m) : + m[a]? = some (m[a]'h') := + ExtDHashMap.Const.get?_eq_some_get h' + +theorem getElem_eq_get_getElem? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + m[a]'h = m[a]?.get (mem_iff_isSome_getElem?.mp h) := + ExtDHashMap.Const.get_eq_get_get? (h := h) + +theorem get_getElem? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + m[a]?.get h = m[a]'(mem_iff_isSome_getElem?.mpr h) := + ExtDHashMap.Const.get_get? + +theorem getElem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) {h'} : + m[a]'h' = m[b]'((mem_congr hab).1 h') := + ExtDHashMap.Const.get_congr hab (h' := h') + +@[simp] +theorem getElem!_empty [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + (∅ : ExtHashMap α β)[a]! = default := + ExtDHashMap.Const.get!_empty + +theorem getElem!_insert [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} : + (m.insert k v)[a]! = if k == a then v else m[a]! := + ExtDHashMap.Const.get!_insert + +@[simp] +theorem getElem!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} {v : β} : + (m.insert k v)[k]! = v := + ExtDHashMap.Const.get!_insert_self + +theorem getElem!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited β] + {a : α} : m.contains a = false → m[a]! = default := + ExtDHashMap.Const.get!_eq_default_of_contains_eq_false + +theorem getElem!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + ¬a ∈ m → m[a]! = default := + ExtDHashMap.Const.get!_eq_default + +theorem getElem!_erase [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} : + (m.erase k)[a]! = if k == a then default else m[a]! := + ExtDHashMap.Const.get!_erase + +@[simp] +theorem getElem!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited β] {k : α} : + (m.erase k)[k]! = default := + ExtDHashMap.Const.get!_erase_self + +theorem getElem?_eq_some_getElem!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited β] + {a : α} : m.contains a = true → m[a]? = some m[a]! := + ExtDHashMap.Const.get?_eq_some_get!_of_contains + +theorem getElem?_eq_some_getElem! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + a ∈ m → m[a]? = some m[a]! := + ExtDHashMap.Const.get?_eq_some_get! + +theorem getElem!_eq_get!_getElem? [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + m[a]! = m[a]?.get! := + ExtDHashMap.Const.get!_eq_get!_get? + +theorem getElem_eq_getElem! [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} {h'} : + m[a]'h' = m[a]! := + @ExtDHashMap.Const.get_eq_get! _ _ _ _ _ _ _ _ _ h' + +theorem getElem!_congr [EquivBEq α] [LawfulHashable α] [Inhabited β] {a b : α} (hab : a == b) : + m[a]! = m[b]! := + ExtDHashMap.Const.get!_congr hab + +@[simp] +theorem getD_empty [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + (∅ : ExtHashMap α β).getD a fallback = fallback := + ExtDHashMap.Const.getD_empty + +theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} : + (m.insert k v).getD a fallback = if k == a then v else m.getD a fallback := + ExtDHashMap.Const.getD_insert + +@[simp] +theorem getD_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback v : β} : + (m.insert k v).getD k fallback = v := + ExtDHashMap.Const.getD_insert_self + +theorem getD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} + {fallback : β} : m.contains a = false → m.getD a fallback = fallback := + ExtDHashMap.Const.getD_eq_fallback_of_contains_eq_false + +theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + ¬a ∈ m → m.getD a fallback = fallback := + ExtDHashMap.Const.getD_eq_fallback + +theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : β} : + (m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback := + ExtDHashMap.Const.getD_erase + +@[simp] +theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} : + (m.erase k).getD k fallback = fallback := + ExtDHashMap.Const.getD_erase_self + +theorem getElem?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + m.contains a = true → m[a]? = some (m.getD a fallback) := + ExtDHashMap.Const.get?_eq_some_getD_of_contains + +theorem getElem?_eq_some_getD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + a ∈ m → m[a]? = some (m.getD a fallback) := + ExtDHashMap.Const.get?_eq_some_getD + +theorem getD_eq_getD_getElem? [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} : + m.getD a fallback = m[a]?.getD fallback := + ExtDHashMap.Const.getD_eq_getD_get? + +theorem getElem_eq_getD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : β} {h'} : + m[a]'h' = m.getD a fallback := + @ExtDHashMap.Const.get_eq_getD _ _ _ _ _ _ _ _ _ h' + +theorem getElem!_eq_getD_default [EquivBEq α] [LawfulHashable α] [Inhabited β] {a : α} : + m[a]! = m.getD a default := + ExtDHashMap.Const.get!_eq_getD_default + +theorem getD_congr [EquivBEq α] [LawfulHashable α] {a b : α} {fallback : β} (hab : a == b) : + m.getD a fallback = m.getD b fallback := + ExtDHashMap.Const.getD_congr hab + +@[simp] +theorem getKey?_empty [EquivBEq α] [LawfulHashable α] {a : α} : + (∅ : ExtHashMap α β).getKey? a = none := + ExtDHashMap.getKey?_empty + +theorem getKey?_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insert k v).getKey? a = if k == a then some k else m.getKey? a := + ExtDHashMap.getKey?_insert + +@[simp] +theorem getKey?_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v).getKey? k = some k := + ExtDHashMap.getKey?_insert_self + +theorem contains_eq_isSome_getKey? [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = (m.getKey? a).isSome := + ExtDHashMap.contains_eq_isSome_getKey? + +theorem mem_iff_isSome_getKey? [EquivBEq α] [LawfulHashable α] {a : α} : + a ∈ m ↔ (m.getKey? a).isSome := + ExtDHashMap.mem_iff_isSome_getKey? + +theorem mem_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] {k k' : α} + (h : m.getKey? k = some k') : k' ∈ m := + ExtDHashMap.mem_of_getKey?_eq_some h + +theorem getKey?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = false → m.getKey? a = none := + ExtDHashMap.getKey?_eq_none_of_contains_eq_false + +theorem getKey?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m → m.getKey? a = none := + ExtDHashMap.getKey?_eq_none + +theorem getKey?_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).getKey? a = if k == a then none else m.getKey? a := + ExtDHashMap.getKey?_erase + +@[simp] +theorem getKey?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).getKey? k = none := + ExtDHashMap.getKey?_erase_self + +theorem getKey?_beq [EquivBEq α] [LawfulHashable α] {k : α} : (m.getKey? k).all (· == k) := + ExtDHashMap.getKey?_beq + +theorem getKey?_congr [EquivBEq α] [LawfulHashable α] {k k' : α} (h : k == k') : + m.getKey? k = m.getKey? k' := + ExtDHashMap.getKey?_congr h + +theorem getKey?_eq_some_of_contains [LawfulBEq α] {k : α} (h : m.contains k) : + m.getKey? k = some k := + ExtDHashMap.getKey?_eq_some h + +theorem getKey?_eq_some [LawfulBEq α] {k : α} (h : k ∈ m) : m.getKey? k = some k := by + simpa only [mem_iff_contains] using getKey?_eq_some_of_contains h + +theorem getKey_insert [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} : + (m.insert k v)[a]'h₁ = + if h₂ : k == a then v else m[a]'(mem_of_mem_insert h₁ (Bool.eq_false_iff.2 h₂)) := + ExtDHashMap.Const.get_insert (h₁ := h₁) + +@[simp] +theorem getKey_insert_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insert k v).getKey k mem_insert_self = k := + ExtDHashMap.getKey_insert_self + +@[simp] +theorem getKey_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} : + (m.erase k).getKey a h' = m.getKey a (mem_of_mem_erase h') := + ExtDHashMap.getKey_erase (h' := h') + +theorem getKey?_eq_some_getKey [EquivBEq α] [LawfulHashable α] {a : α} (h : a ∈ m) : + m.getKey? a = some (m.getKey a h) := + ExtDHashMap.getKey?_eq_some_getKey h + +theorem getKey_eq_get_getKey? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + m.getKey a h = (m.getKey? a).get (mem_iff_isSome_getKey?.mp h) := + ExtDHashMap.getKey_eq_get_getKey? + +theorem get_getKey? [EquivBEq α] [LawfulHashable α] {a : α} {h} : + (m.getKey? a).get h = m.getKey a (mem_iff_isSome_getKey?.mpr h) := + ExtDHashMap.get_getKey? + +theorem getKey_beq [EquivBEq α] [LawfulHashable α] {k : α} (h : k ∈ m) : m.getKey k h == k := + ExtDHashMap.getKey_beq h + +theorem getKey_congr [EquivBEq α] [LawfulHashable α] {k₁ k₂ : α} (h : k₁ == k₂) + (h₁ : k₁ ∈ m) : m.getKey k₁ h₁ = m.getKey k₂ ((mem_congr h).mp h₁) := + ExtDHashMap.getKey_congr h h₁ + +theorem getKey_eq [LawfulBEq α] {k : α} (h : k ∈ m) : m.getKey k h = k := + ExtDHashMap.getKey_eq h + +@[simp] +theorem getKey!_empty [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + (∅ : ExtHashMap α β).getKey! a = default := + ExtDHashMap.getKey!_empty + +theorem getKey!_insert [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β} : + (m.insert k v).getKey! a = if k == a then k else m.getKey! a := + ExtDHashMap.getKey!_insert + +@[simp] +theorem getKey!_insert_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} {v : β} : + (m.insert k v).getKey! k = k := + ExtDHashMap.getKey!_insert_self + +theorem getKey!_eq_default_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α] + {a : α} : m.contains a = false → m.getKey! a = default := + ExtDHashMap.getKey!_eq_default_of_contains_eq_false + +theorem getKey!_eq_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + ¬a ∈ m → m.getKey! a = default := + ExtDHashMap.getKey!_eq_default + +theorem getKey!_erase [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} : + (m.erase k).getKey! a = if k == a then default else m.getKey! a := + ExtDHashMap.getKey!_erase + +@[simp] +theorem getKey!_erase_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} : + (m.erase k).getKey! k = default := + ExtDHashMap.getKey!_erase_self + +theorem getKey?_eq_some_getKey!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α] + {a : α} : m.contains a = true → m.getKey? a = some (m.getKey! a) := + ExtDHashMap.getKey?_eq_some_getKey!_of_contains + +theorem getKey?_eq_some_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + a ∈ m → m.getKey? a = some (m.getKey! a) := + ExtDHashMap.getKey?_eq_some_getKey! + +theorem getKey!_eq_get!_getKey? [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.getKey! a = (m.getKey? a).get! := + ExtDHashMap.getKey!_eq_get!_getKey? + +theorem getKey_eq_getKey! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {h'} : + m.getKey a h' = m.getKey! a := + @ExtDHashMap.getKey_eq_getKey! _ _ _ _ _ _ _ _ _ h' + +theorem getKey!_congr [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} (h : k == k') : + m.getKey! k = m.getKey! k' := + ExtDHashMap.getKey!_congr h + +theorem getKey!_eq_of_contains [LawfulBEq α] [Inhabited α] {k : α} (h : m.contains k) : + m.getKey! k = k := + ExtDHashMap.getKey!_eq_of_contains h + +theorem getKey!_eq_of_mem [LawfulBEq α] [Inhabited α] {k : α} (h : k ∈ m) : m.getKey! k = k := + ExtDHashMap.getKey!_eq_of_mem h + +@[simp] +theorem getKeyD_empty [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} : + (∅ : ExtHashMap α β).getKeyD a fallback = fallback := + ExtDHashMap.getKeyD_empty + +theorem getKeyD_insert [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β} : + (m.insert k v).getKeyD a fallback = if k == a then k else m.getKeyD a fallback := + ExtDHashMap.getKeyD_insert + +@[simp] +theorem getKeyD_insert_self [EquivBEq α] [LawfulHashable α] {k fallback : α} {v : β} : + (m.insert k v).getKeyD k fallback = k := + ExtDHashMap.getKeyD_insert_self + +theorem getKeyD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} + {fallback : α} : m.contains a = false → m.getKeyD a fallback = fallback := + ExtDHashMap.getKeyD_eq_fallback_of_contains_eq_false + +theorem getKeyD_eq_fallback [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} : + ¬a ∈ m → m.getKeyD a fallback = fallback := + ExtDHashMap.getKeyD_eq_fallback + +theorem getKeyD_erase [EquivBEq α] [LawfulHashable α] {k a : α} {fallback : α} : + (m.erase k).getKeyD a fallback = if k == a then fallback else m.getKeyD a fallback := + ExtDHashMap.getKeyD_erase + +@[simp] +theorem getKeyD_erase_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : α} : + (m.erase k).getKeyD k fallback = fallback := + ExtDHashMap.getKeyD_erase_self + +theorem getKey?_eq_some_getKeyD_of_contains [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} : + m.contains a = true → m.getKey? a = some (m.getKeyD a fallback) := + ExtDHashMap.getKey?_eq_some_getKeyD_of_contains + +theorem getKey?_eq_some_getKeyD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} : + a ∈ m → m.getKey? a = some (m.getKeyD a fallback) := + ExtDHashMap.getKey?_eq_some_getKeyD + +theorem getKeyD_eq_getD_getKey? [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} : + m.getKeyD a fallback = (m.getKey? a).getD fallback := + ExtDHashMap.getKeyD_eq_getD_getKey? + +theorem getKey_eq_getKeyD [EquivBEq α] [LawfulHashable α] {a : α} {fallback : α} {h'} : + m.getKey a h' = m.getKeyD a fallback := + @ExtDHashMap.getKey_eq_getKeyD _ _ _ _ _ _ _ _ _ h' + +theorem getKey!_eq_getKeyD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.getKey! a = m.getKeyD a default := + ExtDHashMap.getKey!_eq_getKeyD_default + +theorem getKeyD_congr [EquivBEq α] [LawfulHashable α] {k k' fallback : α} + (h : k == k') : m.getKeyD k fallback = m.getKeyD k' fallback := + ExtDHashMap.getKeyD_congr h + +theorem getKeyD_eq_of_contains [LawfulBEq α] {k fallback : α} (h : m.contains k) : + m.getKeyD k fallback = k := + ExtDHashMap.getKeyD_eq_of_contains h + +theorem getKeyD_eq_of_mem [LawfulBEq α] {k fallback : α} (h : k ∈ m) : + m.getKeyD k fallback = k := + ExtDHashMap.getKeyD_eq_of_mem h + +@[simp] +theorem not_insertIfNew_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + ¬m.insertIfNew k v = ∅ := + (not_congr ext_iff).mpr ExtDHashMap.not_insertIfNew_eq_empty + +@[simp] +theorem contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insertIfNew k v).contains a = (k == a || m.contains a) := + ExtDHashMap.contains_insertIfNew + +@[simp] +theorem mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + a ∈ m.insertIfNew k v ↔ k == a ∨ a ∈ m := + ExtDHashMap.mem_insertIfNew + +theorem contains_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insertIfNew k v).contains k := + ExtDHashMap.contains_insertIfNew_self + +theorem mem_insertIfNew_self [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + k ∈ m.insertIfNew k v := + ExtDHashMap.mem_insertIfNew_self + +theorem contains_of_contains_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insertIfNew k v).contains a → (k == a) = false → m.contains a := + ExtDHashMap.contains_of_contains_insertIfNew + +theorem mem_of_mem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + a ∈ m.insertIfNew k v → (k == a) = false → a ∈ m := + ExtDHashMap.mem_of_mem_insertIfNew + +/-- This is a restatement of `contains_of_contains_insertIfNew` that is written to exactly match the proof +obligation in the statement of `getElem_insertIfNew`. -/ +theorem contains_of_contains_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insertIfNew k v).contains a → ¬((k == a) ∧ m.contains k = false) → m.contains a := + ExtDHashMap.contains_of_contains_insertIfNew' + +/-- This is a restatement of `mem_of_mem_insertIfNew` that is written to exactly match the proof obligation +in the statement of `getElem_insertIfNew`. -/ +theorem mem_of_mem_insertIfNew' [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + a ∈ m.insertIfNew k v → ¬((k == a) ∧ ¬k ∈ m) → a ∈ m := + ExtDHashMap.mem_of_mem_insertIfNew' + +theorem size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insertIfNew k v).size = if k ∈ m then m.size else m.size + 1 := + ExtDHashMap.size_insertIfNew + +theorem size_le_size_insertIfNew [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + m.size ≤ (m.insertIfNew k v).size := + ExtDHashMap.size_le_size_insertIfNew + +theorem size_insertIfNew_le [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (m.insertIfNew k v).size ≤ m.size + 1 := + ExtDHashMap.size_insertIfNew_le + +theorem getElem?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + (m.insertIfNew k v)[a]? = if k == a ∧ ¬k ∈ m then some v else m[a]? := + ExtDHashMap.Const.get?_insertIfNew + +theorem getElem_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} : + (m.insertIfNew k v)[a]'h₁ = + if h₂ : k == a ∧ ¬k ∈ m then v else m[a]'(mem_of_mem_insertIfNew' h₁ h₂) := + ExtDHashMap.Const.get_insertIfNew (h₁ := h₁) + +theorem getElem!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited β] {k a : α} {v : β} : + (m.insertIfNew k v)[a]! = if k == a ∧ ¬k ∈ m then v else m[a]! := + ExtDHashMap.Const.get!_insertIfNew + +theorem getD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {fallback v : β} : + (m.insertIfNew k v).getD a fallback = + if k == a ∧ ¬k ∈ m then v else m.getD a fallback := + ExtDHashMap.Const.getD_insertIfNew + +theorem getKey?_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} : + getKey? (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then some k else getKey? m a := + ExtDHashMap.getKey?_insertIfNew + +theorem getKey_insertIfNew [EquivBEq α] [LawfulHashable α] {k a : α} {v : β} {h₁} : + getKey (m.insertIfNew k v) a h₁ = + if h₂ : k == a ∧ ¬k ∈ m then k else getKey m a (mem_of_mem_insertIfNew' h₁ h₂) := + ExtDHashMap.getKey_insertIfNew + +theorem getKey!_insertIfNew [EquivBEq α] [LawfulHashable α] [Inhabited α] {k a : α} {v : β} : + getKey! (m.insertIfNew k v) a = if k == a ∧ ¬k ∈ m then k else getKey! m a := + ExtDHashMap.getKey!_insertIfNew + +theorem getKeyD_insertIfNew [EquivBEq α] [LawfulHashable α] {k a fallback : α} {v : β} : + getKeyD (m.insertIfNew k v) a fallback = if k == a ∧ ¬k ∈ m then k else getKeyD m a fallback := + ExtDHashMap.getKeyD_insertIfNew + +@[simp] +theorem getThenInsertIfNew?_fst [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (getThenInsertIfNew? m k v).1 = get? m k := + ExtDHashMap.Const.getThenInsertIfNew?_fst + +@[simp] +theorem getThenInsertIfNew?_snd [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + (getThenInsertIfNew? m k v).2 = m.insertIfNew k v := + ext (ExtDHashMap.Const.getThenInsertIfNew?_snd) + +instance [EquivBEq α] [LawfulHashable α] : LawfulGetElem (ExtHashMap α β) α β (fun m a => a ∈ m) where + getElem?_def m a _ := by + split + · exact getElem?_eq_some_getElem ‹_› + · exact getElem?_eq_none ‹_› + getElem!_def m a := by + rw [getElem!_eq_get!_getElem?] + split <;> simp_all + +variable {ρ : Type w} [ForIn Id ρ (α × β)] + +@[simp] +theorem insertMany_nil [EquivBEq α] [LawfulHashable α] : + insertMany m [] = m := + ext ExtDHashMap.Const.insertMany_nil + +@[simp] +theorem insertMany_list_singleton [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + insertMany m [⟨k, v⟩] = m.insert k v := + ext ExtDHashMap.Const.insertMany_list_singleton + +theorem insertMany_cons [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {k : α} {v : β} : + insertMany m (⟨k, v⟩ :: l) = insertMany (m.insert k v) l := + ext ExtDHashMap.Const.insertMany_cons + +@[elab_as_elim] +theorem insertMany_ind [EquivBEq α] [LawfulHashable α] + {motive : ExtHashMap α β → Prop} (m : ExtHashMap α β) {l : ρ} + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l) := + show motive ⟨ExtDHashMap.Const.insertMany m.1 l⟩ from + ExtDHashMap.Const.insertMany_ind m.inner l init fun m => insert ⟨m⟩ + +@[simp] +theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + (insertMany m l).contains k = (m.contains k || (l.map Prod.fst).contains k) := + ExtDHashMap.Const.contains_insertMany_list + +@[simp] +theorem mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + k ∈ insertMany m l ↔ k ∈ m ∨ (l.map Prod.fst).contains k := + ExtDHashMap.Const.mem_insertMany_list + +theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} (mem : k ∈ insertMany m l) + (contains_eq_false : (l.map Prod.fst).contains k = false) : + k ∈ m := + ExtDHashMap.Const.mem_of_mem_insertMany_list mem contains_eq_false + +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} : k ∈ m → k ∈ m.insertMany l := + ExtDHashMap.Const.mem_insertMany_of_mem + +theorem getElem?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l)[k]? = m[k]? := + ExtDHashMap.Const.get?_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getElem?_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) : + (insertMany m l)[k']? = some v := + ExtDHashMap.Const.get?_insertMany_list_of_mem k_beq distinct mem + +theorem getElem_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) + {h} : + (insertMany m l)[k] = m[k]'(mem_of_mem_insertMany_list h contains_eq_false) := + ExtDHashMap.Const.get_insertMany_list_of_contains_eq_false contains_eq_false (h := h) + +theorem getElem_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) {h} : + (insertMany m l)[k'] = v := + ExtDHashMap.Const.get_insertMany_list_of_mem k_beq distinct mem (h := h) + +theorem getElem!_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited β] {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l)[k]! = m[k]! := + ExtDHashMap.Const.get!_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getElem!_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited β] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) : + (insertMany m l)[k']! = v := + ExtDHashMap.Const.get!_insertMany_list_of_mem k_beq distinct mem + +theorem getD_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} {fallback : β} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + getD (insertMany m l) k fallback = getD m k fallback := + ExtDHashMap.Const.getD_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v fallback : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) (mem : ⟨k, v⟩ ∈ l) : + getD (insertMany m l) k' fallback = v := + ExtDHashMap.Const.getD_insertMany_list_of_mem k_beq distinct mem + +theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l).getKey? k = m.getKey? k := + ExtDHashMap.Const.getKey?_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey?_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (insertMany m l).getKey? k' = some k := + ExtDHashMap.Const.getKey?_insertMany_list_of_mem k_beq distinct mem + +theorem getKey_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) + {h} : + (insertMany m l).getKey k h = + m.getKey k (mem_of_mem_insertMany_list h contains_eq_false) := + ExtDHashMap.Const.getKey_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) + {h} : + (insertMany m l).getKey k' h = k := + ExtDHashMap.Const.getKey_insertMany_list_of_mem k_beq distinct mem + +theorem getKey!_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l).getKey! k = m.getKey! k := + ExtDHashMap.Const.getKey!_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKey!_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (insertMany m l).getKey! k' = k := + ExtDHashMap.Const.getKey!_insertMany_list_of_mem k_beq distinct mem + +theorem getKeyD_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k fallback : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (insertMany m l).getKeyD k fallback = m.getKeyD k fallback := + ExtDHashMap.Const.getKeyD_insertMany_list_of_contains_eq_false contains_eq_false + +theorem getKeyD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (insertMany m l).getKeyD k' fallback = k := + ExtDHashMap.Const.getKeyD_insertMany_list_of_mem k_beq distinct mem + +theorem size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (∀ (a : α), a ∈ m → (l.map Prod.fst).contains a = false) → + (insertMany m l).size = m.size + l.length := + ExtDHashMap.Const.size_insertMany_list distinct + +theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} : + m.size ≤ (insertMany m l).size := + ExtDHashMap.Const.size_le_size_insertMany_list + +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertMany m l).size := + ExtDHashMap.Const.size_le_size_insertMany + +theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} : + (insertMany m l).size ≤ m.size + l.length := + ExtDHashMap.Const.size_insertMany_list_le + +@[simp] +theorem insertMany_list_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List (α × β)} : + m.insertMany l = ∅ ↔ m = ∅ ∧ l = [] := by + simpa only [ext_iff] using ExtDHashMap.Const.insertMany_list_eq_empty_iff + +theorem eq_empty_of_insertMany_eq_empty [EquivBEq α] [LawfulHashable α] {l : ρ} : + m.insertMany l = ∅ → m = ∅ := by + simpa only [ext_iff] using ExtDHashMap.Const.eq_empty_of_insertMany_eq_empty + +variable {m : ExtHashMap α Unit} +variable {ρ : Type w} [ForIn Id ρ α] + +@[simp] +theorem insertManyIfNewUnit_nil [EquivBEq α] [LawfulHashable α] : + insertManyIfNewUnit m [] = m := + ext ExtDHashMap.Const.insertManyIfNewUnit_nil + +@[simp] +theorem insertManyIfNewUnit_list_singleton [EquivBEq α] [LawfulHashable α] {k : α} : + insertManyIfNewUnit m [k] = m.insertIfNew k () := + ext ExtDHashMap.Const.insertManyIfNewUnit_list_singleton + +theorem insertManyIfNewUnit_cons [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : + insertManyIfNewUnit m (k :: l) = insertManyIfNewUnit (m.insertIfNew k ()) l := + ext ExtDHashMap.Const.insertManyIfNewUnit_cons + +@[elab_as_elim] +theorem insertManyIfNewUnit_ind [EquivBEq α] [LawfulHashable α] + {motive : ExtHashMap α Unit → Prop} (m : ExtHashMap α Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l) := + show motive ⟨ExtDHashMap.Const.insertManyIfNewUnit m.1 l⟩ from + ExtDHashMap.Const.insertManyIfNewUnit_ind m.inner l init fun m => insert ⟨m⟩ + +@[simp] +theorem contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (insertManyIfNewUnit m l).contains k = (m.contains k || l.contains k) := + ExtDHashMap.Const.contains_insertManyIfNewUnit_list + +@[simp] +theorem mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + k ∈ insertManyIfNewUnit m l ↔ k ∈ m ∨ l.contains k := + ExtDHashMap.Const.mem_insertManyIfNewUnit_list + +theorem mem_of_mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (contains_eq_false : l.contains k = false) : + k ∈ insertManyIfNewUnit m l → k ∈ m := + ExtDHashMap.Const.mem_of_mem_insertManyIfNewUnit_list contains_eq_false + +theorem mem_insertManyIfNewUnit_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} : k ∈ m → k ∈ insertManyIfNewUnit m l := + ExtDHashMap.Const.mem_insertManyIfNewUnit_of_mem + +theorem getElem?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (insertManyIfNewUnit m l)[k]? = + if k ∈ m ∨ l.contains k then some () else none := + ExtDHashMap.Const.get?_insertManyIfNewUnit_list + +theorem getElem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {h} : + (insertManyIfNewUnit m l)[k] = () := + rfl + +theorem getElem!_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (insertManyIfNewUnit m l)[k]! = () := + rfl + +theorem getD_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {fallback : Unit} : + getD (insertManyIfNewUnit m l) k fallback = () := by + rfl + +theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getKey? (insertManyIfNewUnit m l) k = none := + ExtDHashMap.Const.getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + not_mem contains_eq_false + +theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getKey? (insertManyIfNewUnit m l) k' = some k := + ExtDHashMap.Const.getKey?_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem getKey?_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (mem : k ∈ m) : + getKey? (insertManyIfNewUnit m l) k = getKey? m k := + ExtDHashMap.Const.getKey?_insertManyIfNewUnit_list_of_mem mem + +theorem getKey_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) {h} : + getKey (insertManyIfNewUnit m l) k' h = k := + ExtDHashMap.Const.getKey_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem getKey_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (mem : k ∈ m) {h} : + getKey (insertManyIfNewUnit m l) k h = getKey m k mem := + ExtDHashMap.Const.getKey_insertManyIfNewUnit_list_of_mem mem + +theorem getKey!_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + [EquivBEq α] [LawfulHashable α] [Inhabited α] {l : List α} {k : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getKey! (insertManyIfNewUnit m l) k = default := + ExtDHashMap.Const.getKey!_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + not_mem contains_eq_false + +theorem getKey!_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getKey! (insertManyIfNewUnit m l) k' = k := + ExtDHashMap.Const.getKey!_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem getKey!_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} (mem : k ∈ m) : + getKey! (insertManyIfNewUnit m l) k = getKey! m k := + ExtDHashMap.Const.getKey!_insertManyIfNewUnit_list_of_mem mem + +theorem getKeyD_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + [EquivBEq α] [LawfulHashable α] {l : List α} {k fallback : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getKeyD (insertManyIfNewUnit m l) k fallback = fallback := + ExtDHashMap.Const.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + not_mem contains_eq_false + +theorem getKeyD_insertManyIfNewUnit_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' fallback : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l ) : + getKeyD (insertManyIfNewUnit m l) k' fallback = k := + ExtDHashMap.Const.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem getKeyD_insertManyIfNewUnit_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} (mem : k ∈ m) : + getKeyD (insertManyIfNewUnit m l) k fallback = getKeyD m k fallback := + ExtDHashMap.Const.getKeyD_insertManyIfNewUnit_list_of_mem mem + +theorem size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} + (distinct : l.Pairwise (fun a b => (a == b) = false)) : + (∀ (a : α), a ∈ m → l.contains a = false) → + (insertManyIfNewUnit m l).size = m.size + l.length := + ExtDHashMap.Const.size_insertManyIfNewUnit_list distinct + +theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] + {l : List α} : + m.size ≤ (insertManyIfNewUnit m l).size := + ExtDHashMap.Const.size_le_size_insertManyIfNewUnit_list + +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertManyIfNewUnit m l).size := + ExtDHashMap.Const.size_le_size_insertManyIfNewUnit + +theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] + {l : List α} : + (insertManyIfNewUnit m l).size ≤ m.size + l.length := + ExtDHashMap.Const.size_insertManyIfNewUnit_list_le + +@[simp] +theorem insertManyIfNewUnit_list_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List α} : + insertManyIfNewUnit m l = ∅ ↔ m = ∅ ∧ l = [] := by + simpa only [ext_iff] using ExtDHashMap.Const.insertManyIfNewUnit_list_eq_empty_iff + +theorem eq_empty_of_insertManyIfNewUnit_eq_empty [EquivBEq α] [LawfulHashable α] {l : ρ} : + insertManyIfNewUnit m l = ∅ → m = ∅ := by + simpa only [ext_iff] using ExtDHashMap.Const.eq_empty_of_insertManyIfNewUnit_eq_empty + +end + +section + +@[simp] +theorem ofList_nil [EquivBEq α] [LawfulHashable α] : + ofList ([] : List (α × β)) = ∅ := + ext ExtDHashMap.Const.ofList_nil + +@[simp] +theorem ofList_singleton [EquivBEq α] [LawfulHashable α] {k : α} {v : β} : + ofList [⟨k, v⟩] = (∅ : ExtHashMap α β).insert k v := + ext ExtDHashMap.Const.ofList_singleton + +theorem ofList_cons [EquivBEq α] [LawfulHashable α] {k : α} {v : β} {tl : List (α × β)} : + ofList (⟨k, v⟩ :: tl) = insertMany ((∅ : ExtHashMap α β).insert k v) tl := + ext ExtDHashMap.Const.ofList_cons + +@[simp] +theorem contains_ofList [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + (ofList l).contains k = (l.map Prod.fst).contains k := + ExtDHashMap.Const.contains_ofList + +@[simp] +theorem mem_ofList [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} : + k ∈ ofList l ↔ (l.map Prod.fst).contains k := + ExtDHashMap.Const.mem_ofList + +theorem getElem?_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l)[k]? = none := + ExtDHashMap.Const.get?_ofList_of_contains_eq_false contains_eq_false + +theorem getElem?_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (ofList l)[k']? = some v := + ExtDHashMap.Const.get?_ofList_of_mem k_beq distinct mem + +theorem getElem_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) + {h} : + (ofList l)[k'] = v := + ExtDHashMap.Const.get_ofList_of_mem k_beq distinct mem (h := h) + +theorem getElem!_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} [Inhabited β] + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l)[k]! = (default : β) := + ExtDHashMap.Const.get!_ofList_of_contains_eq_false contains_eq_false + +theorem getElem!_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} [Inhabited β] + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + (ofList l)[k']! = v := + ExtDHashMap.Const.get!_ofList_of_mem k_beq distinct mem + +theorem getD_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} {fallback : β} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + getD (ofList l) k fallback = fallback := + ExtDHashMap.Const.getD_ofList_of_contains_eq_false contains_eq_false + +theorem getD_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k k' : α} (k_beq : k == k') {v : β} {fallback : β} + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : ⟨k, v⟩ ∈ l) : + getD (ofList l) k' fallback = v := + ExtDHashMap.Const.getD_ofList_of_mem k_beq distinct mem + +theorem getKey?_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l).getKey? k = none := + ExtDHashMap.Const.getKey?_ofList_of_contains_eq_false contains_eq_false + +theorem getKey?_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (ofList l).getKey? k' = some k := + ExtDHashMap.Const.getKey?_ofList_of_mem k_beq distinct mem + +theorem getKey_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) + {h} : + (ofList l).getKey k' h = k := + ExtDHashMap.Const.getKey_ofList_of_mem k_beq distinct mem + +theorem getKey!_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List (α × β)} {k : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l).getKey! k = default := + ExtDHashMap.Const.getKey!_ofList_of_contains_eq_false contains_eq_false + +theorem getKey!_ofList_of_mem [EquivBEq α] [LawfulHashable α] [Inhabited α] + {l : List (α × β)} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (ofList l).getKey! k' = k := + ExtDHashMap.Const.getKey!_ofList_of_mem k_beq distinct mem + +theorem getKeyD_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} {k fallback : α} + (contains_eq_false : (l.map Prod.fst).contains k = false) : + (ofList l).getKeyD k fallback = fallback := + ExtDHashMap.Const.getKeyD_ofList_of_contains_eq_false contains_eq_false + +theorem getKeyD_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} + {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) + (mem : k ∈ l.map Prod.fst) : + (ofList l).getKeyD k' fallback = k := + ExtDHashMap.Const.getKeyD_ofList_of_mem k_beq distinct mem + +theorem size_ofList [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} (distinct : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (ofList l).size = l.length := + ExtDHashMap.Const.size_ofList distinct + +theorem size_ofList_le [EquivBEq α] [LawfulHashable α] + {l : List (α × β)} : + (ofList l).size ≤ l.length := + ExtDHashMap.Const.size_ofList_le + +@[simp] +theorem ofList_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List (α × β)} : + ofList l = ∅ ↔ l = [] := + ext_iff.trans ExtDHashMap.Const.ofList_eq_empty_iff + +@[simp] +theorem unitOfList_nil [EquivBEq α] [LawfulHashable α] : + unitOfList ([] : List α) = ∅ := + ext ExtDHashMap.Const.unitOfList_nil + +@[simp] +theorem unitOfList_singleton [EquivBEq α] [LawfulHashable α] {k : α} : + unitOfList [k] = (∅ : ExtHashMap α Unit).insertIfNew k () := + ext ExtDHashMap.Const.unitOfList_singleton + +theorem unitOfList_cons [EquivBEq α] [LawfulHashable α] {hd : α} {tl : List α} : + unitOfList (hd :: tl) = + insertManyIfNewUnit ((∅ : ExtHashMap α Unit).insertIfNew hd ()) tl := + ext ExtDHashMap.Const.unitOfList_cons + +@[simp] +theorem contains_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (unitOfList l).contains k = l.contains k := + ExtDHashMap.Const.contains_unitOfList + +@[simp] +theorem mem_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + k ∈ unitOfList l ↔ l.contains k := + ExtDHashMap.Const.mem_unitOfList + +@[simp] +theorem getElem?_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (unitOfList l)[k]? = + if l.contains k then some () else none := + ExtDHashMap.Const.get?_unitOfList + +@[simp] +theorem getElem_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {h} : + (unitOfList l)[k] = () := + rfl + +@[simp] +theorem getElem!_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (unitOfList l)[k]! = () := + rfl + +@[simp] +theorem getD_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} {fallback : Unit} : + getD (unitOfList l) k fallback = () := + rfl + +theorem getKey?_unitOfList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (contains_eq_false : l.contains k = false) : + getKey? (unitOfList l) k = none := + ExtDHashMap.Const.getKey?_unitOfList_of_contains_eq_false contains_eq_false + +theorem getKey?_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getKey? (unitOfList l) k' = some k := + ExtDHashMap.Const.getKey?_unitOfList_of_mem k_beq distinct mem + +theorem getKey_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) {h} : + getKey (unitOfList l) k' h = k := + ExtDHashMap.Const.getKey_unitOfList_of_mem k_beq distinct mem + +theorem getKey!_unitOfList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} + (contains_eq_false : l.contains k = false) : + getKey! (unitOfList l) k = default := + ExtDHashMap.Const.getKey!_unitOfList_of_contains_eq_false contains_eq_false + +theorem getKey!_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + getKey! (unitOfList l) k' = k := + ExtDHashMap.Const.getKey!_unitOfList_of_mem k_beq distinct mem + +theorem getKeyD_unitOfList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} + (contains_eq_false : l.contains k = false) : + getKeyD (unitOfList l) k fallback = fallback := + ExtDHashMap.Const.getKeyD_unitOfList_of_contains_eq_false contains_eq_false + +theorem getKeyD_unitOfList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + getKeyD (unitOfList l) k' fallback = k := + ExtDHashMap.Const.getKeyD_unitOfList_of_mem k_beq distinct mem + +theorem size_unitOfList [EquivBEq α] [LawfulHashable α] + {l : List α} + (distinct : l.Pairwise (fun a b => (a == b) = false)) : + (unitOfList l).size = l.length := + ExtDHashMap.Const.size_unitOfList distinct + +theorem size_unitOfList_le [EquivBEq α] [LawfulHashable α] + {l : List α} : + (unitOfList l).size ≤ l.length := + ExtDHashMap.Const.size_unitOfList_le + +@[simp] +theorem unitOfList_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List α} : + unitOfList l = ∅ ↔ l = [] := + ext_iff.trans ExtDHashMap.Const.unitOfList_eq_empty_iff + +end + +section Alter + +variable {m : ExtHashMap α β} + +theorem alter_eq_empty_iff_erase_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + alter m k f = ∅ ↔ m.erase k = ∅ ∧ f m[k]? = none := by + simpa only [ext_iff] using ExtDHashMap.Const.alter_eq_empty_iff_erase_eq_empty + +@[simp] +theorem alter_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + alter m k f = ∅ ↔ (m = ∅ ∨ (m.size = 1 ∧ k ∈ m)) ∧ f m[k]? = none := by + simpa only [ext_iff] using ExtDHashMap.Const.alter_eq_empty_iff + +theorem contains_alter [EquivBEq α] [LawfulHashable α] {k k': α} {f : Option β → Option β} : + (alter m k f).contains k' = if k == k' then (f m[k]?).isSome else m.contains k' := + ExtDHashMap.Const.contains_alter + +theorem mem_alter [EquivBEq α] [LawfulHashable α] {k k': α} {f : Option β → Option β} : + k' ∈ alter m k f ↔ if k == k' then (f m[k]?).isSome = true else k' ∈ m := + ExtDHashMap.Const.mem_alter + +theorem mem_alter_of_beq [EquivBEq α] [LawfulHashable α] {k k': α} {f : Option β → Option β} + (h : k == k') : k' ∈ alter m k f ↔ (f m[k]?).isSome := + ExtDHashMap.Const.mem_alter_of_beq h + +@[simp] +theorem contains_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (alter m k f).contains k = (f m[k]?).isSome := + ExtDHashMap.Const.contains_alter_self + +@[simp] +theorem mem_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + k ∈ alter m k f ↔ (f m[k]?).isSome := + ExtDHashMap.Const.mem_alter_self + +theorem contains_alter_of_beq_eq_false [EquivBEq α] [LawfulHashable α] {k k' : α} + {f : Option β → Option β} (h : (k == k') = false) : + (alter m k f).contains k' = m.contains k' := + ExtDHashMap.Const.contains_alter_of_beq_eq_false h + +theorem mem_alter_of_beq_eq_false [EquivBEq α] [LawfulHashable α] {k k' : α} + {f : Option β → Option β} (h : (k == k') = false) : k' ∈ alter m k f ↔ k' ∈ m := + ExtDHashMap.Const.mem_alter_of_beq_eq_false h + +theorem size_alter [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (m.alter k f).size = + if k ∈ m ∧ (f m[k]?).isNone then + m.size - 1 + else if k ∉ m ∧ (f m[k]?).isSome then + m.size + 1 + else + m.size := + ExtDHashMap.Const.size_alter + +theorem size_alter_eq_add_one [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∉ m) (h' : (f m[k]?).isSome) : + (alter m k f).size = m.size + 1 := + ExtDHashMap.Const.size_alter_eq_add_one h h' + +theorem size_alter_eq_sub_one [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∈ m) (h' : (f m[k]?).isNone) : + (alter m k f).size = m.size - 1 := + ExtDHashMap.Const.size_alter_eq_sub_one h h' + +theorem size_alter_eq_self_of_not_mem [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∉ m) (h' : (f m[k]?).isNone) : + (alter m k f).size = m.size := + ExtDHashMap.Const.size_alter_eq_self_of_not_mem h h' + +theorem size_alter_eq_self_of_mem [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + (h : k ∈ m) (h' : (f m[k]?).isSome) : + (alter m k f).size = m.size := + ExtDHashMap.Const.size_alter_eq_self_of_mem h h' + +theorem size_alter_le_size [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (alter m k f).size ≤ m.size + 1 := + ExtDHashMap.Const.size_alter_le_size + +theorem size_le_size_alter [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + m.size - 1 ≤ (alter m k f).size := + ExtDHashMap.Const.size_le_size_alter + +theorem getElem?_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} : + (alter m k f)[k']? = + if k == k' then + f m[k]? + else + m[k']? := + ExtDHashMap.Const.get?_alter + +@[deprecated getElem?_alter (since := "2025-02-09")] +theorem get?_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} : + get? (alter m k f) k' = + if k == k' then + f (get? m k) + else + get? m k' := + ExtDHashMap.Const.get?_alter + +@[simp] +theorem getElem?_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (alter m k f)[k]? = f m[k]? := + ExtDHashMap.Const.get?_alter_self + +@[deprecated getElem?_alter_self (since := "2025-02-09")] +theorem get?_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + get? (alter m k f) k = f (get? m k) := + ExtDHashMap.Const.get?_alter_self + +theorem getElem_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} + {h : k' ∈ alter m k f} : + (alter m k f)[k'] = + if heq : k == k' then + haveI h' : (f m[k]?).isSome := mem_alter_of_beq heq |>.mp h + f m[k]? |>.get h' + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + m[(k')]'h' := + ExtDHashMap.Const.get_alter (h := h) + +@[deprecated getElem_alter (since := "2025-02-09")] +theorem get_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} + {h : k' ∈ alter m k f} : + get (alter m k f) k' h = + if heq : k == k' then + haveI h' : (f (get? m k)).isSome := mem_alter_of_beq heq |>.mp h + f (get? m k) |>.get h' + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + get m k' h' := + ExtDHashMap.Const.get_alter + +@[simp] +theorem getElem_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + {h : k ∈ alter m k f} : + haveI h' : (f m[k]?).isSome := mem_alter_self.mp h + (alter m k f)[k] = (f m[k]?).get h' := + ExtDHashMap.Const.get_alter_self (h := h) + +@[deprecated getElem_alter_self (since := "2025-02-09")] +theorem get_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} + {h : k ∈ alter m k f} : + haveI h' : (f (get? m k)).isSome := mem_alter_self.mp h + get (alter m k f) k h = (f (get? m k)).get h' := + ExtDHashMap.Const.get_alter_self + +theorem getElem!_alter [EquivBEq α] [LawfulHashable α] {k k' : α} [Inhabited β] + {f : Option β → Option β} : (alter m k f)[k']! = + if k == k' then + f m[k]? |>.get! + else + m[k']! := + ExtDHashMap.Const.get!_alter + +@[deprecated getElem!_alter (since := "2025-02-09")] +theorem get!_alter [EquivBEq α] [LawfulHashable α] {k k' : α} [Inhabited β] + {f : Option β → Option β} : get! (alter m k f) k' = + if k == k' then + f (get? m k) |>.get! + else + get! m k' := + ExtDHashMap.Const.get!_alter + +@[simp] +theorem getElem!_alter_self [EquivBEq α] [LawfulHashable α] {k : α} [Inhabited β] + {f : Option β → Option β} : (alter m k f)[k]! = (f m[k]?).get! := + ExtDHashMap.Const.get!_alter_self + +@[deprecated getElem!_alter_self (since := "2025-02-09")] +theorem get!_alter_self [EquivBEq α] [LawfulHashable α] {k : α} [Inhabited β] + {f : Option β → Option β} : get! (alter m k f) k = (f (get? m k)).get! := + ExtDHashMap.Const.get!_alter_self + +theorem getD_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {fallback : β} + {f : Option β → Option β} : + getD (alter m k f) k' fallback = + if k == k' then + f m[k]? |>.getD fallback + else + getD m k' fallback := + ExtDHashMap.Const.getD_alter + +@[simp] +theorem getD_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} + {f : Option β → Option β} : + getD (alter m k f) k fallback = (f m[k]?).getD fallback := + ExtDHashMap.Const.getD_alter_self + +theorem getKey?_alter [EquivBEq α] [LawfulHashable α] {k k' : α} {f : Option β → Option β} : + (alter m k f).getKey? k' = + if k == k' then + if (f m[k]?).isSome then some k else none + else + m.getKey? k' := + ExtDHashMap.Const.getKey?_alter + +theorem getKey?_alter_self [EquivBEq α] [LawfulHashable α] {k : α} {f : Option β → Option β} : + (alter m k f).getKey? k = if (f m[k]?).isSome then some k else none := + ExtDHashMap.Const.getKey?_alter_self + +theorem getKey!_alter [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} + {f : Option β → Option β} : (alter m k f).getKey! k' = + if k == k' then + if (f m[k]?).isSome then k else default + else + m.getKey! k' := + ExtDHashMap.Const.getKey!_alter + +theorem getKey!_alter_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} + {f : Option β → Option β} : + (alter m k f).getKey! k = if (f m[k]?).isSome then k else default := + ExtDHashMap.Const.getKey!_alter_self + +theorem getKey_alter [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} + {f : Option β → Option β} {h : k' ∈ alter m k f} : + (alter m k f).getKey k' h = + if heq : k == k' then + k + else + haveI h' : k' ∈ m := mem_alter_of_beq_eq_false (Bool.not_eq_true _ ▸ heq) |>.mp h + m.getKey k' h' := + ExtDHashMap.Const.getKey_alter + +@[simp] +theorem getKey_alter_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} + {f : Option β → Option β} {h : k ∈ alter m k f} : + (alter m k f).getKey k h = k := + ExtDHashMap.Const.getKey_alter_self + +theorem getKeyD_alter [EquivBEq α] [LawfulHashable α] {k k' fallback : α} + {f : Option β → Option β} : + (alter m k f).getKeyD k' fallback = + if k == k' then + if (f m[k]?).isSome then k else fallback + else + m.getKeyD k' fallback := + ExtDHashMap.Const.getKeyD_alter + +theorem getKeyD_alter_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k fallback : α} + {f : Option β → Option β} : + (alter m k f).getKeyD k fallback = if (f m[k]?).isSome then k else fallback := + ExtDHashMap.Const.getKeyD_alter_self + +end Alter + +section Modify + +variable {m : ExtHashMap α β} + +@[simp] +theorem modify_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + modify m k f = ∅ ↔ m = ∅ := by + simpa only [ext_iff] using ExtDHashMap.Const.modify_eq_empty_iff + +@[simp] +theorem contains_modify [EquivBEq α] [LawfulHashable α] {k k': α} {f : β → β} : + (modify m k f).contains k' = m.contains k' := + ExtDHashMap.Const.contains_modify + +@[simp] +theorem mem_modify [EquivBEq α] [LawfulHashable α] {k k': α} {f : β → β} : + k' ∈ modify m k f ↔ k' ∈ m := + ExtDHashMap.Const.mem_modify + +@[simp] +theorem size_modify [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + (modify m k f).size = m.size := + ExtDHashMap.Const.size_modify + +theorem getElem?_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} : + (modify m k f)[k']? = + if k == k' then + m[k]?.map f + else + m[k']? := + ExtDHashMap.Const.get?_modify + +@[deprecated getElem?_modify (since := "2025-02-09")] +theorem get?_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} : + get? (modify m k f) k' = + if k == k' then + get? m k |>.map f + else + get? m k' := + ExtDHashMap.Const.get?_modify + +@[simp] +theorem getElem?_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + (modify m k f)[k]? = m[k]?.map f := + ExtDHashMap.Const.get?_modify_self + +@[deprecated getElem?_modify_self (since := "2025-02-09")] +theorem get?_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + get? (modify m k f) k = (get? m k).map f := + ExtDHashMap.Const.get?_modify_self + +theorem getElem_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} + {h : k' ∈ modify m k f} : + (modify m k f)[k'] = + if heq : k == k' then + haveI h' : k ∈ m := mem_congr heq |>.mpr <| mem_modify.mp h + f m[k] + else + haveI h' : k' ∈ m := mem_modify.mp h + m[k'] := + ExtDHashMap.Const.get_modify (h := h) + +@[deprecated getElem_modify (since := "2025-02-09")] +theorem get_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} + {h : k' ∈ modify m k f} : + get (modify m k f) k' h = + if heq : k == k' then + haveI h' : k ∈ m := mem_congr heq |>.mpr <| mem_modify.mp h + f (get m k h') + else + haveI h' : k' ∈ m := mem_modify.mp h + get m k' h' := + ExtDHashMap.Const.get_modify + +@[simp] +theorem getElem_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} + {h : k ∈ modify m k f} : + haveI h' : k ∈ m := mem_modify.mp h + (modify m k f)[k] = f m[k] := + ExtDHashMap.Const.get_modify_self (h := h) + +@[deprecated getElem_modify_self (since := "2025-02-09")] +theorem get_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} + {h : k ∈ modify m k f} : + haveI h' : k ∈ m := mem_modify.mp h + get (modify m k f) k h = f (get m k h') := + ExtDHashMap.Const.get_modify_self + +theorem getElem!_modify [EquivBEq α] [LawfulHashable α] {k k' : α} [Inhabited β] {f : β → β} : + (modify m k f)[k']! = + if k == k' then + m[k]?.map f |>.get! + else + m[k']! := + ExtDHashMap.Const.get!_modify + +@[deprecated getElem!_modify (since := "2025-02-09")] +theorem get!_modify [EquivBEq α] [LawfulHashable α] {k k' : α} [Inhabited β] {f : β → β} : + get! (modify m k f) k' = + if k == k' then + get? m k |>.map f |>.get! + else + get! m k' := + ExtDHashMap.Const.get!_modify + +@[simp] +theorem getElem!_modify_self [EquivBEq α] [LawfulHashable α] {k : α} [Inhabited β] {f : β → β} : + (modify m k f)[k]! = (m[k]?.map f).get! := + ExtDHashMap.Const.get!_modify_self + +@[deprecated getElem!_modify_self (since := "2025-02-09")] +theorem get!_modify_self [EquivBEq α] [LawfulHashable α] {k : α} [Inhabited β] {f : β → β} : + get! (modify m k f) k = ((get? m k).map f).get! := + ExtDHashMap.Const.get!_modify_self + +theorem getD_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {fallback : β} {f : β → β} : + getD (modify m k f) k' fallback = + if k == k' then + m[k]?.map f |>.getD fallback + else + getD m k' fallback := + ExtDHashMap.Const.getD_modify + +@[simp] +theorem getD_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {fallback : β} {f : β → β} : + getD (modify m k f) k fallback = (m[k]?.map f).getD fallback := + ExtDHashMap.Const.getD_modify_self + +theorem getKey?_modify [EquivBEq α] [LawfulHashable α] {k k' : α} {f : β → β} : + (modify m k f).getKey? k' = + if k == k' then + if k ∈ m then some k else none + else + m.getKey? k' := + ExtDHashMap.Const.getKey?_modify + +theorem getKey?_modify_self [EquivBEq α] [LawfulHashable α] {k : α} {f : β → β} : + (modify m k f).getKey? k = if k ∈ m then some k else none := + ExtDHashMap.Const.getKey?_modify_self + +theorem getKey!_modify [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} {f : β → β} : + (modify m k f).getKey! k' = + if k == k' then + if k ∈ m then k else default + else + m.getKey! k' := + ExtDHashMap.Const.getKey!_modify + +theorem getKey!_modify_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} {f : β → β} : + (modify m k f).getKey! k = if k ∈ m then k else default := + ExtDHashMap.Const.getKey!_modify_self + +theorem getKey_modify [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} {f : β → β} + {h : k' ∈ modify m k f} : + (modify m k f).getKey k' h = + if k == k' then + k + else + haveI h' : k' ∈ m := mem_modify.mp h + m.getKey k' h' := + ExtDHashMap.Const.getKey_modify + +@[simp] +theorem getKey_modify_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k : α} {f : β → β} + {h : k ∈ modify m k f} : (modify m k f).getKey k h = k := + ExtDHashMap.Const.getKey_modify_self + +theorem getKeyD_modify [EquivBEq α] [LawfulHashable α] {k k' fallback : α} {f : β → β} : + (modify m k f).getKeyD k' fallback = + if k == k' then + if k ∈ m then k else fallback + else + m.getKeyD k' fallback := + ExtDHashMap.Const.getKeyD_modify + +theorem getKeyD_modify_self [EquivBEq α] [LawfulHashable α] [Inhabited α] {k fallback : α} + {f : β → β} : (modify m k f).getKeyD k fallback = if k ∈ m then k else fallback := + ExtDHashMap.Const.getKeyD_modify_self + +end Modify + +section Ext + +variable {m₁ m₂ : ExtHashMap α β} + +@[ext 900] +theorem ext_getKey_getElem? [EquivBEq α] [LawfulHashable α] + {m₁ m₂ : ExtHashMap α β} + (hk : ∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') + (hv : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ = m₂ := + ext (ExtDHashMap.Const.ext_getKey_get? hk hv) + +@[ext] +theorem ext_getElem? [LawfulBEq α] {m₁ m₂ : ExtHashMap α β} + (h : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ = m₂ := + ext (ExtDHashMap.Const.ext_get? h) + +theorem ext_getKey?_unit [EquivBEq α] [LawfulHashable α] + {m₁ m₂ : ExtHashMap α Unit} (h : ∀ k, m₁.getKey? k = m₂.getKey? k) : m₁ = m₂ := + ext (ExtDHashMap.Const.ext_getKey?_unit h) + +theorem ext_contains_unit [LawfulBEq α] + {m₁ m₂ : ExtHashMap α Unit} (h : ∀ k, m₁.contains k = m₂.contains k) : m₁ = m₂ := + ext (ExtDHashMap.Const.ext_contains_unit h) + +theorem ext_mem_unit [LawfulBEq α] + {m₁ m₂ : ExtHashMap α Unit} (h : ∀ k, k ∈ m₁ ↔ k ∈ m₂) : m₁ = m₂ := + ext (ExtDHashMap.Const.ext_mem_unit h) + +end Ext + +section filterMap + +variable {m : ExtHashMap α β} + +theorem filterMap_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → β → Option γ} : + m.filterMap f = ∅ ↔ ∀ k h, f (m.getKey k h) (m[k]'h) = none := + ext_iff.trans ExtDHashMap.Const.filterMap_eq_empty_iff + +theorem mem_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + k ∈ m.filterMap f ↔ ∃ h, (f (m.getKey k h) m[k]).isSome := + ExtDHashMap.Const.mem_filterMap + +theorem contains_of_contains_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + (m.filterMap f).contains k = true → m.contains k = true := + ExtDHashMap.contains_of_contains_filterMap + +theorem mem_of_mem_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + k ∈ m.filterMap f → k ∈ m := + ExtDHashMap.mem_of_mem_filterMap + +theorem size_filterMap_le_size [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} : + (m.filterMap f).size ≤ m.size := + ExtDHashMap.size_filterMap_le_size + +theorem size_filterMap_eq_size_iff [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} : + (m.filterMap f).size = m.size ↔ ∀ k h, (f (m.getKey k h) m[k]).isSome := + ExtDHashMap.Const.size_filterMap_eq_size_iff + +theorem getElem?_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + (m.filterMap f)[k]? = m[k]?.pbind (fun x h' => + f (m.getKey k (mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))) x) := + ExtDHashMap.Const.get?_filterMap + +theorem getElem?_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k k' : α} (h : m.getKey? k = some k') : + (m.filterMap f)[k]? = m[k]?.bind (f k') := + ExtDHashMap.Const.get?_filterMap_of_getKey?_eq_some h + +theorem isSome_apply_of_mem_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + ∀ (h : k ∈ m.filterMap f), + (f (m.getKey k (mem_of_mem_filterMap h)) + (m[k]'(mem_of_mem_filterMap h))).isSome := + ExtDHashMap.Const.isSome_apply_of_mem_filterMap + +@[simp] +theorem getElem_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} {h} : + (m.filterMap f)[k]'h = + (f (m.getKey k (mem_of_mem_filterMap h)) + (m[k]'(mem_of_mem_filterMap h))).get + (isSome_apply_of_mem_filterMap h) := + ExtDHashMap.Const.get_filterMap (h := h) + +theorem getElem!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → Option γ} {k : α} : + (m.filterMap f)[k]! = + (m[k]?.pbind (fun x h' => + f (m.getKey k (mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))) x)).get! := + ExtDHashMap.Const.get!_filterMap + +theorem getElem!_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → Option γ} {k k' : α} (h : m.getKey? k = some k') : + (m.filterMap f)[k]! = (m[k]?.bind (f k')).get! := + ExtDHashMap.Const.get!_filterMap_of_getKey?_eq_some h + +theorem getD_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} {fallback : γ} : + (m.filterMap f).getD k fallback = + (m[k]?.pbind (fun x h' => + f (m.getKey k (mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback := + ExtDHashMap.Const.getD_filterMap + +theorem getD_filterMap_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k k' : α} {fallback : γ} (h : m.getKey? k = some k') : + (m.filterMap f).getD k fallback = (m[k]?.bind (f k')).getD fallback := + ExtDHashMap.Const.getD_filterMap_of_getKey?_eq_some h + +theorem getKey?_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} : + (m.filterMap f).getKey? k = + (m.getKey? k).pfilter (fun x h' => + (f x (m[x]'(mem_of_getKey?_eq_some h'))).isSome) := + ExtDHashMap.Const.getKey?_filterMap + +@[simp] +theorem getKey_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k : α} {h'} : + (m.filterMap f).getKey k h' = m.getKey k (mem_of_mem_filterMap h') := + ExtDHashMap.getKey_filterMap + +theorem getKey!_filterMap [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → β → Option γ} {k : α} : + (m.filterMap f).getKey! k = + ((m.getKey? k).pfilter (fun x h' => + (f x (m[x]'(mem_of_getKey?_eq_some h'))).isSome)).get! := + ExtDHashMap.Const.getKey!_filterMap + +theorem getKeyD_filterMap [EquivBEq α] [LawfulHashable α] + {f : α → β → Option γ} {k fallback : α} : + (m.filterMap f).getKeyD k fallback = + ((m.getKey? k).pfilter (fun x h' => + (f x (m[x]'(mem_of_getKey?_eq_some h'))).isSome)).getD fallback := + ExtDHashMap.Const.getKeyD_filterMap + +end filterMap + +section filter + +variable {m : ExtHashMap α β} + +theorem filterMap_eq_filter [EquivBEq α] [LawfulHashable α] {f : α → β → Bool} : + (m.filterMap (fun k => Option.guard (fun v => f k v))) = m.filter f := + ext ExtDHashMap.filterMap_eq_filter + +theorem filter_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → β → Bool} : + m.filter f = ∅ ↔ ∀ k h, f (m.getKey k h) (m[k]'h) = false := + ext_iff.trans ExtDHashMap.Const.filter_eq_empty_iff + +theorem mem_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + k ∈ m.filter f ↔ ∃ (h' : k ∈ m), f (m.getKey k h') m[k] := + ExtDHashMap.Const.mem_filter + +theorem contains_of_contains_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + (m.filter f).contains k = true → m.contains k = true := + ExtDHashMap.contains_of_contains_filter + +theorem mem_of_mem_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + k ∈ m.filter f → k ∈ m := + ExtDHashMap.mem_of_mem_filter + +theorem size_filter_le_size [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} : + (m.filter f).size ≤ m.size := + ExtDHashMap.size_filter_le_size + +theorem size_filter_eq_size_iff [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} : + (m.filter f).size = m.size ↔ ∀ k h, f (m.getKey k h) (m.get k h) := + ExtDHashMap.Const.size_filter_eq_size_iff + +theorem filter_eq_self_iff [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} : + m.filter f = m ↔ ∀ k h, f (m.getKey k h) (m.get k h) := + ext_iff.trans ExtDHashMap.Const.filter_eq_self_iff + +theorem getElem?_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + (m.filter f)[k]? = m[k]?.pfilter (fun x h' => + f (m.getKey k (mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))) x) := + ExtDHashMap.Const.get?_filter + +theorem getElem?_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k k' : α} : + m.getKey? k = some k' → + (m.filter f)[k]? = m[k]?.filter (fun x => f k' x) := + ExtDHashMap.Const.get?_filter_of_getKey?_eq_some + +@[simp] +theorem getElem_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} {h'} : + (m.filter f)[k]'(h') = m[k]'(mem_of_mem_filter h') := + ExtDHashMap.Const.get_filter (h' := h') + +theorem getElem!_filter [EquivBEq α] [LawfulHashable α] [Inhabited β] + {f : α → β → Bool} {k : α} : + (m.filter f)[k]! = + (m[k]?.pfilter (fun x h' => + f (m.getKey k (mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))) x)).get! := + ExtDHashMap.Const.get!_filter + +theorem getElem!_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited β] + {f : α → β → Bool} {k k' : α} : + m.getKey? k = some k' → + (m.filter f)[k]! = (m[k]?.filter (f k')).get! := + ExtDHashMap.Const.get!_filter_of_getKey?_eq_some + +theorem getD_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} {fallback : β} : + (m.filter f).getD k fallback = (m[k]?.pfilter (fun x h' => + f (m.getKey k (mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))) x)).getD fallback := + ExtDHashMap.Const.getD_filter + +theorem getD_filter_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k k' : α} {fallback : β} : + m.getKey? k = some k' → + (m.filter f).getD k fallback = + (m[k]?.filter (fun x => f k' x)).getD fallback := + ExtDHashMap.Const.getD_filter_of_getKey?_eq_some + +theorem getKey?_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} : + (m.filter f).getKey? k = + (m.getKey? k).pfilter (fun x h' => + (f x (m[x]'(mem_of_getKey?_eq_some h')))) := + ExtDHashMap.Const.getKey?_filter + +theorem getKey?_filter_key [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} : + (m.filter fun k _ => f k).getKey? k = (m.getKey? k).filter f := + ExtDHashMap.getKey?_filter_key + +@[simp] +theorem getKey_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k : α} {h'} : + (m.filter f).getKey k h' = m.getKey k (mem_of_mem_filter h') := + ExtDHashMap.getKey_filter + +theorem getKey!_filter [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → β → Bool} {k : α} : + (m.filter f).getKey! k = + ((m.getKey? k).pfilter (fun x h' => + (f x (m[x]'(mem_of_getKey?_eq_some h'))))).get! := + ExtDHashMap.Const.getKey!_filter + +theorem getKey!_filter_key [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → Bool} {k : α} : + (m.filter fun k _ => f k).getKey! k = ((m.getKey? k).filter f).get! := + ExtDHashMap.getKey!_filter_key + +theorem getKeyD_filter [EquivBEq α] [LawfulHashable α] + {f : α → β → Bool} {k fallback : α} : + (m.filter f).getKeyD k fallback = + ((m.getKey? k).pfilter (fun x h' => + (f x (m[x]'(mem_of_getKey?_eq_some h'))))).getD fallback := + ExtDHashMap.Const.getKeyD_filter + +theorem getKeyD_filter_key [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k fallback : α} : + (m.filter fun k _ => f k).getKeyD k fallback = ((m.getKey? k).filter f).getD fallback := + ExtDHashMap.getKeyD_filter_key + +end filter + +section map + +variable {m : ExtHashMap α β} + +@[simp] +theorem map_id_fun [EquivBEq α] [LawfulHashable α] : m.map (fun _ v => v) = m := + ext ExtDHashMap.map_id_fun + +@[simp] +theorem map_map [EquivBEq α] [LawfulHashable α] {f : α → β → γ} {g : α → γ → δ} : + (m.map f).map g = m.map fun k v => g k (f k v) := + ext ExtDHashMap.map_map + +theorem filterMap_equiv_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} : + (m.filterMap (fun k v => some (f k v))) = m.map f := + ext ExtDHashMap.filterMap_eq_map + +@[simp] +theorem map_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → β → γ} : + m.map f = ∅ ↔ m = ∅ := by + simpa only [ext_iff] using ExtDHashMap.map_eq_empty_iff + +@[simp] +theorem contains_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + (m.map f).contains k = m.contains k := + ExtDHashMap.contains_map + +theorem contains_of_contains_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + (m.map f).contains k = true → m.contains k = true := + ExtDHashMap.contains_of_contains_map + +@[simp] +theorem mem_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + k ∈ m.map f ↔ k ∈ m := by + simp only [mem_iff_contains, contains_map] + +theorem mem_of_mem_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + k ∈ m.map f → k ∈ m := + ExtDHashMap.contains_of_contains_map + +@[simp] +theorem size_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} : + (m.map f).size = m.size := + ExtDHashMap.size_map + +theorem getElem?_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + (m.map f)[k]? = m[k]?.pmap (fun v h' => f (m.getKey k h') v) + (fun _ h' => mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h')) := + ExtDHashMap.Const.get?_map + +theorem getElem?_map_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k k' : α} (h : m.getKey? k = some k') : + (m.map f)[k]? = m[k]?.map (f k') := + ExtDHashMap.Const.get?_map_of_getKey?_eq_some h + +@[simp] +theorem getElem_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} {h'} : + (m.map f)[k]'(h') = + f (m.getKey k (mem_of_mem_map h')) (m[k]'(mem_of_mem_map h')) := + ExtDHashMap.Const.get_map (h' := h') + +theorem getElem!_map [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → γ} {k : α} : + (m.map f)[k]! = + (m[k]?.pmap (fun v h => f (m.getKey k h) v) + (fun _ h' => mem_iff_isSome_getElem?.mpr (Option.isSome_of_mem h'))).get! := + ExtDHashMap.Const.get!_map + +theorem getElem!_map_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → γ} {k k' : α} (h : m.getKey? k = some k') : + (m.map f)[k]! = (m[k]?.map (f k')).get! := + ExtDHashMap.Const.get!_map_of_getKey?_eq_some h + +theorem getD_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} {fallback : γ} : + (m.map f).getD k fallback = + (m[k]?.pmap (fun v h => f (m.getKey k h) v) + (fun _ h' => mem_iff_isSome_getElem?.mpr (Option.isSome_of_eq_some h'))).getD fallback := + ExtDHashMap.Const.getD_map + +theorem getD_map_of_getKey?_eq_some [EquivBEq α] [LawfulHashable α] [Inhabited γ] + {f : α → β → γ} {k k' : α} {fallback : γ} (h : m.getKey? k = some k') : + (m.map f).getD k fallback = (m[k]?.map (f k')).getD fallback := + ExtDHashMap.Const.getD_map_of_getKey?_eq_some h + +@[simp] +theorem getKey?_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} : + (m.map f).getKey? k = m.getKey? k := + ExtDHashMap.getKey?_map + +@[simp] +theorem getKey_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k : α} {h'} : + (m.map f).getKey k h' = m.getKey k (mem_of_mem_map h') := + ExtDHashMap.getKey_map + +@[simp] +theorem getKey!_map [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → β → γ} {k : α} : + (m.map f).getKey! k = m.getKey! k := + ExtDHashMap.getKey!_map + +@[simp] +theorem getKeyD_map [EquivBEq α] [LawfulHashable α] + {f : α → β → γ} {k fallback : α} : + (m.map f).getKeyD k fallback = m.getKeyD k fallback := + ExtDHashMap.getKeyD_map + +end map + +end Std.ExtHashMap diff --git a/src/Std/Data/ExtHashSet.lean b/src/Std/Data/ExtHashSet.lean new file mode 100644 index 0000000000..ef3e2ff1a0 --- /dev/null +++ b/src/Std/Data/ExtHashSet.lean @@ -0,0 +1,8 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtHashSet.Basic +import Std.Data.ExtHashSet.Lemmas diff --git a/src/Std/Data/ExtHashSet/Basic.lean b/src/Std/Data/ExtHashSet/Basic.lean new file mode 100644 index 0000000000..f74708fa06 --- /dev/null +++ b/src/Std/Data/ExtHashSet/Basic.lean @@ -0,0 +1,202 @@ +/- +Copyright (c) 2025 Robin Arnez. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robin Arnez +-/ +prelude +import Std.Data.ExtHashMap.Basic + +/-! +# Extensional hash sets + +This module develops the type `Std.ExtHashSet` of extensional hash sets. + +Lemmas about the operations on `Std.ExtHashSet` are available in the +module `Std.Data.ExtHashSet.Lemmas`. +-/ + +set_option linter.missingDocs true +set_option autoImplicit false + +universe u v + +variable {α : Type u} {_ : BEq α} {_ : Hashable α} + +namespace Std + +/-- +Hash sets. + +This is a simple separate-chaining hash table. The data of the hash set consists of a cached size +and an array of buckets, where each bucket is a linked list of keys. The number of buckets +is always a power of two. The hash set doubles its size upon inserting an element such that the +number of elements is more than 75% of the number of buckets. + +The hash table is backed by an `Array`. Users should make sure that the hash set is used linearly to +avoid expensive copies. + +The hash set uses `==` (provided by the `BEq` typeclass) to compare elements and `hash` (provided by +the `Hashable` typeclass) to hash them. To ensure that the operations behave as expected, `==` +should be an equivalence relation and `a == b` should imply `hash a = hash b` (see also the +`EquivBEq` and `LawfulHashable` typeclasses). Both of these conditions are automatic if the BEq +instance is lawful, i.e., if `a == b` implies `a = b`. + +In contrast to regular hash sets, `Std.ExtHashSet` offers several extensionality lemmas +and therefore has more lemmas about equality of hash maps. This however also makes it lose the +ability to iterate freely over hash sets. + +These hash sets contain a bundled well-formedness invariant, which means that they cannot +be used in nested inductive types. For these use cases, `Std.HashSet.Raw` and +`Std.HashSet.Raw.WF` unbundle the invariant from the hash set. When in doubt, prefer +`HashSet` or `ExtHashSet` over `HashSet.Raw`. +-/ +structure ExtHashSet (α : Type u) [BEq α] [Hashable α] where + /-- Internal implementation detail of the hash set. -/ + inner : ExtHashMap α Unit + +namespace ExtHashSet + +/-- +Creates a new empty hash set. The optional parameter `capacity` can be supplied to presize the +set so that it can hold the given number of elements without reallocating. It is also possible to +use the empty collection notations `∅` and `{}` to create an empty hash set with the default +capacity. +-/ +@[inline] def emptyWithCapacity [BEq α] [Hashable α] (capacity := 8) : ExtHashSet α := + ⟨ExtHashMap.emptyWithCapacity capacity⟩ + +instance [BEq α] [Hashable α] : EmptyCollection (ExtHashSet α) where + emptyCollection := emptyWithCapacity + +instance [BEq α] [Hashable α] : Inhabited (ExtHashSet α) where + default := ∅ + +/-- +Inserts the given element into the set. If the hash set already contains an element that is +equal (with regard to `==`) to the given element, then the hash set is returned unchanged. + +Note: this non-replacement behavior is true for `ExtHashSet` and `ExtHashSet.Raw`. +The `insert` function on `ExtHashMap`, `DExtHashMap`, `ExtHashMap.Raw` and `DExtHashMap.Raw` behaves +differently: it will overwrite an existing mapping. +-/ +@[inline] def insert [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) (a : α) : ExtHashSet α := + ⟨m.inner.insertIfNew a ()⟩ + +instance [EquivBEq α] [LawfulHashable α] : Singleton α (ExtHashSet α) where + singleton a := (∅ : ExtHashSet α).insert a + +instance [EquivBEq α] [LawfulHashable α] : Insert α (ExtHashSet α) where + insert a s := s.insert a + +/-- +Checks whether an element is present in a set and inserts the element if it was not found. +If the hash set already contains an element that is equal (with regard to `==`) to the given +element, then the hash set is returned unchanged. + +Equivalent to (but potentially faster than) calling `contains` followed by `insert`. +-/ +@[inline] +def containsThenInsert [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) + (a : α) : Bool × ExtHashSet α := + let ⟨replaced, r⟩ := m.inner.containsThenInsertIfNew a () + ⟨replaced, ⟨r⟩⟩ + +/-- +Returns `true` if the given key is present in the set. There is also a `Prop`-valued version of +this: `a ∈ m` is equivalent to `m.contains a = true`. + +Observe that this is different behavior than for lists: for lists, `∈` uses `=` and `contains` use +`==` for comparisons, while for hash sets, both use `==`. +-/ +@[inline] def contains [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) (a : α) : Bool := + m.inner.contains a + +instance [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] : Membership α (ExtHashSet α) where + mem m a := a ∈ m.inner + +instance [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] + {m : ExtHashSet α} {a : α} : Decidable (a ∈ m) := + inferInstanceAs (Decidable (a ∈ m.inner)) + +/-- Removes the element if it exists. -/ +@[inline] def erase [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) (a : α) : ExtHashSet α := + ⟨m.inner.erase a⟩ + +/-- The number of elements present in the set -/ +@[inline] def size [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) : Nat := + m.inner.size + +/-- +Checks if given key is contained and returns the key if it is, otherwise `none`. +The result in the `some` case is guaranteed to be pointer equal to the key in the set. +-/ +@[inline] def get? [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) (a : α) : Option α := + m.inner.getKey? a + +/-- +Retrieves the key from the set that matches `a`. Ensures that such a key exists by requiring a proof +of `a ∈ m`. The result is guaranteed to be pointer equal to the key in the set. +-/ +@[inline] def get [EquivBEq α] [LawfulHashable α] + (m : ExtHashSet α) (a : α) (h : a ∈ m) : α := + m.inner.getKey a h + +/-- +Checks if given key is contained and returns the key if it is, otherwise `fallback`. +If they key is contained the result is guaranteed to be pointer equal to the key in the set. +-/ +@[inline] def getD [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) (a : α) (fallback : α) : α := + m.inner.getKeyD a fallback + +/-- +Checks if given key is contained and returns the key if it is, otherwise panics. +If no panic occurs the result is guaranteed to be pointer equal to the key in the set. +-/ +@[inline] def get! [EquivBEq α] [LawfulHashable α] [Inhabited α] (m : ExtHashSet α) (a : α) : α := + m.inner.getKey! a + +/-- +Returns `true` if the hash set contains no elements. + +Note that if your `BEq` instance is not reflexive or your `Hashable` instance is not +lawful, then it is possible that this function returns `false` even though `m.contains a = false` +for all `a`. +-/ +@[inline] def isEmpty [EquivBEq α] [LawfulHashable α] (m : ExtHashSet α) : Bool := + m.inner.isEmpty + +/-- +Creates a hash set from a list of elements. Note that unlike repeatedly calling `insert`, if the +collection contains multiple elements that are equal (with regard to `==`), then the last element +in the collection will be present in the returned hash set. +-/ +@[inline] def ofList [BEq α] [Hashable α] (l : List α) : ExtHashSet α := + ⟨ExtHashMap.unitOfList l⟩ + +/-- Removes all elements from the hash set for which the given function returns `false`. -/ +@[inline] def filter [EquivBEq α] [LawfulHashable α] (f : α → Bool) (m : ExtHashSet α) : ExtHashSet α := + ⟨m.inner.filter fun a _ => f a⟩ + +/-- +Inserts multiple mappings into the hash set by iterating over the given collection and calling +`insert`. If the same key appears multiple times, the first occurrence takes precedence. + +Note: this precedence behavior is true for `ExtHashSet` and `ExtHashSet.Raw`. The `insertMany` function on +`ExtHashMap`, `DExtHashMap`, `ExtHashMap.Raw` and `DExtHashMap.Raw` behaves differently: it will prefer the last +appearance. +-/ +@[inline] def insertMany [EquivBEq α] [LawfulHashable α] {ρ : Type v} [ForIn Id ρ α] + (m : ExtHashSet α) (l : ρ) : ExtHashSet α := + ⟨m.inner.insertManyIfNewUnit l⟩ + +/-- +Creates a hash set from an array of elements. Note that unlike repeatedly calling `insert`, if the +collection contains multiple elements that are equal (with regard to `==`), then the last element +in the collection will be present in the returned hash set. +-/ +@[inline] def ofArray [BEq α] [Hashable α] (l : Array α) : ExtHashSet α := + ⟨ExtHashMap.unitOfArray l⟩ + +end ExtHashSet + +end Std diff --git a/src/Std/Data/ExtHashSet/Lemmas.lean b/src/Std/Data/ExtHashSet/Lemmas.lean new file mode 100644 index 0000000000..6fa412b893 --- /dev/null +++ b/src/Std/Data/ExtHashSet/Lemmas.lean @@ -0,0 +1,694 @@ +/- +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 Std.Data.ExtHashMap.Lemmas +import Std.Data.ExtHashSet.Basic + +/-! +# Extensional hash set lemmas + +This module contains lemmas about `Std.ExtHashSet`. +-/ + +set_option linter.missingDocs true +set_option autoImplicit false + +universe u v + +variable {α : Type u} {_ : BEq α} {_ : Hashable α} + +namespace Std.ExtHashSet + +section + +variable {m : ExtHashSet α} + +private theorem ext {m m' : ExtHashSet α} : m.inner = m'.inner → m = m' := by + cases m; cases m'; rintro rfl; rfl + +private theorem ext_iff {m m' : ExtHashSet α} : m = m' ↔ m.inner = m'.inner := + ⟨fun h => h ▸ rfl, ext⟩ + +@[simp] +theorem isEmpty_iff [EquivBEq α] [LawfulHashable α] : m.isEmpty ↔ m = ∅ := + ExtHashMap.isEmpty_iff.trans ext_iff.symm + +@[simp] +theorem isEmpty_eq_false_iff [EquivBEq α] [LawfulHashable α] : m.isEmpty = false ↔ ¬m = ∅ := + (Bool.not_eq_true _).symm.to_iff.trans (not_congr isEmpty_iff) + +@[simp] +theorem empty_eq : ∅ = m ↔ m = ∅ := eq_comm + +@[simp] +theorem emptyWithCapacity_eq [EquivBEq α] [LawfulHashable α] {c} : (emptyWithCapacity c : ExtHashSet α) = ∅ := + ext ExtHashMap.emptyWithCapacity_eq + +@[simp] +theorem not_insert_eq_empty [EquivBEq α] [LawfulHashable α] {k : α} : + ¬m.insert k = ∅ := + (not_congr ext_iff).mpr ExtHashMap.not_insertIfNew_eq_empty + +theorem mem_iff_contains [EquivBEq α] [LawfulHashable α] {a : α} : a ∈ m ↔ m.contains a := + ExtHashMap.mem_iff_contains + +theorem contains_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : + m.contains a = m.contains b := + ExtHashMap.contains_congr hab + +theorem mem_congr [EquivBEq α] [LawfulHashable α] {a b : α} (hab : a == b) : a ∈ m ↔ b ∈ m := + ExtHashMap.mem_congr hab + +@[simp] theorem contains_empty [EquivBEq α] [LawfulHashable α] {a : α} : + (∅ : ExtHashSet α).contains a = false := + ExtHashMap.contains_empty + +@[simp] theorem not_mem_empty [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ (∅ : ExtHashSet α) := + ExtHashMap.not_mem_empty + +theorem eq_empty_iff_forall_contains [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ ∀ a, m.contains a = false := + ext_iff.trans ExtHashMap.eq_empty_iff_forall_contains + +theorem eq_empty_iff_forall_not_mem [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ ∀ a, ¬a ∈ m := + ext_iff.trans ExtHashMap.eq_empty_iff_forall_not_mem + +@[simp] theorem insert_eq_insert [EquivBEq α] [LawfulHashable α] {a : α} : + Insert.insert a m = m.insert a := + rfl + +@[simp] theorem singleton_eq_insert [EquivBEq α] [LawfulHashable α] {a : α} : + Singleton.singleton a = (∅ : ExtHashSet α).insert a := + rfl + +@[simp] +theorem contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.insert k).contains a = (k == a || m.contains a) := + ExtHashMap.contains_insertIfNew + +@[simp] +theorem mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.insert k ↔ k == a ∨ a ∈ m := + ExtHashMap.mem_insertIfNew + +theorem contains_of_contains_insert [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.insert k).contains a → (k == a) = false → m.contains a := + ExtHashMap.contains_of_contains_insertIfNew + +theorem mem_of_mem_insert [EquivBEq α] [LawfulHashable α] {k a : α} : + a ∈ m.insert k → (k == a) = false → a ∈ m := + ExtHashMap.mem_of_mem_insertIfNew + +/-- This is a restatement of `contains_insert` that is written to exactly match the proof +obligation in the statement of `get_insert`. -/ +theorem contains_of_contains_insert' [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.insert k).contains a → ¬((k == a) ∧ m.contains k = false) → m.contains a := + ExtHashMap.contains_of_contains_insertIfNew' + +/-- This is a restatement of `mem_insert` that is written to exactly match the proof obligation +in the statement of `get_insert`. -/ +theorem mem_of_mem_insert' [EquivBEq α] [LawfulHashable α] {k a : α} : + a ∈ m.insert k → ¬((k == a) ∧ ¬k ∈ m) → a ∈ m := + ExtHashMap.mem_of_mem_insertIfNew' + +theorem contains_insert_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.insert k).contains k := by + simp + +theorem mem_insert_self [EquivBEq α] [LawfulHashable α] {k : α} : k ∈ m.insert k := by simp + +@[simp] +theorem size_empty [EquivBEq α] [LawfulHashable α] : (∅ : ExtHashSet α).size = 0 := + ExtHashMap.size_empty + +theorem eq_empty_iff_size_eq_zero [EquivBEq α] [LawfulHashable α] : m = ∅ ↔ m.size = 0 := + ext_iff.trans ExtHashMap.eq_empty_iff_size_eq_zero + +theorem size_insert [EquivBEq α] [LawfulHashable α] {k : α} : + (m.insert k).size = if k ∈ m then m.size else m.size + 1 := + ExtHashMap.size_insertIfNew + +theorem size_le_size_insert [EquivBEq α] [LawfulHashable α] {k : α} : m.size ≤ (m.insert k).size := + ExtHashMap.size_le_size_insertIfNew + +theorem size_insert_le [EquivBEq α] [LawfulHashable α] {k : α} : + (m.insert k).size ≤ m.size + 1 := + ExtHashMap.size_insertIfNew_le + +@[simp] +theorem erase_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : ExtHashSet α).erase a = ∅ := + ext ExtHashMap.erase_empty + +@[simp] +theorem erase_eq_empty_iff [EquivBEq α] [LawfulHashable α] {k : α} : + m.erase k = ∅ ↔ m = ∅ ∨ m.size = 1 ∧ k ∈ m := by + simpa only [ext_iff] using ExtHashMap.erase_eq_empty_iff + +@[simp] +theorem contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).contains a = (!(k == a) && m.contains a) := + ExtHashMap.contains_erase + +@[simp] +theorem mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + a ∈ m.erase k ↔ (k == a) = false ∧ a ∈ m := + ExtHashMap.mem_erase + +theorem contains_of_contains_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).contains a → m.contains a := + ExtHashMap.contains_of_contains_erase + +theorem mem_of_mem_erase [EquivBEq α] [LawfulHashable α] {k a : α} : a ∈ m.erase k → a ∈ m := + ExtHashMap.mem_of_mem_erase + +theorem size_erase [EquivBEq α] [LawfulHashable α] {k : α} : + (m.erase k).size = if k ∈ m then m.size - 1 else m.size := + ExtHashMap.size_erase + +theorem size_erase_le [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).size ≤ m.size := + ExtHashMap.size_erase_le + +theorem size_le_size_erase [EquivBEq α] [LawfulHashable α] {k : α} : + m.size ≤ (m.erase k).size + 1 := + ExtHashMap.size_le_size_erase + +@[simp] +theorem get?_empty [EquivBEq α] [LawfulHashable α] {a : α} : (∅ : ExtHashSet α).get? a = none := + ExtHashMap.getKey?_empty + +theorem get?_insert [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.insert k).get? a = if k == a ∧ ¬k ∈ m then some k else m.get? a := + ExtHashMap.getKey?_insertIfNew + +theorem contains_eq_isSome_get? [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = (m.get? a).isSome := + ExtHashMap.contains_eq_isSome_getKey? + +theorem mem_iff_isSome_get? [EquivBEq α] [LawfulHashable α] {a : α} : + a ∈ m ↔ (m.get? a).isSome := + ExtHashMap.mem_iff_isSome_getKey? + +theorem get?_eq_none_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = false → m.get? a = none := + ExtHashMap.getKey?_eq_none_of_contains_eq_false + +theorem get?_eq_none [EquivBEq α] [LawfulHashable α] {a : α} : ¬a ∈ m → m.get? a = none := + ExtHashMap.getKey?_eq_none + +theorem get?_erase [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).get? a = if k == a then none else m.get? a := + ExtHashMap.getKey?_erase + +@[simp] +theorem get?_erase_self [EquivBEq α] [LawfulHashable α] {k : α} : (m.erase k).get? k = none := + ExtHashMap.getKey?_erase_self + +theorem get?_beq [EquivBEq α] [LawfulHashable α] {k : α} : (m.get? k).all (· == k) := + ExtHashMap.getKey?_beq + +theorem get?_congr [EquivBEq α] [LawfulHashable α] {k k' : α} (h : k == k') : + m.get? k = m.get? k' := + ExtHashMap.getKey?_congr h + +theorem get?_eq_some_of_contains [LawfulBEq α] {k : α} (h : m.contains k) : m.get? k = some k := + ExtHashMap.getKey?_eq_some_of_contains h + +theorem get?_eq_some [LawfulBEq α] {k : α} (h : k ∈ m) : m.get? k = some k := + ExtHashMap.getKey?_eq_some h + +theorem get_insert [EquivBEq α] [LawfulHashable α] {k a : α} {h₁} : + (m.insert k).get a h₁ = + if h₂ : k == a ∧ ¬k ∈ m then k else m.get a (mem_of_mem_insert' h₁ h₂) := + ExtHashMap.getKey_insertIfNew (h₁ := h₁) + +@[simp] +theorem get_erase [EquivBEq α] [LawfulHashable α] {k a : α} {h'} : + (m.erase k).get a h' = m.get a (mem_of_mem_erase h') := + ExtHashMap.getKey_erase (h' := h') + +theorem get?_eq_some_get [EquivBEq α] [LawfulHashable α] {a : α} (h' : a ∈ m) : + m.get? a = some (m.get a h') := + ExtHashMap.getKey?_eq_some_getKey h' + +theorem get_eq_get_get? [EquivBEq α] [LawfulHashable α] {k : α} {h} : + m.get k h = (m.get? k).get (mem_iff_isSome_get?.mp h) := + ExtHashMap.getKey_eq_get_getKey? + +theorem get_get? [EquivBEq α] [LawfulHashable α] {k : α} {h} : + (m.get? k).get h = m.get k (mem_iff_isSome_get?.mpr h) := + ExtHashMap.get_getKey? + +theorem get_beq [EquivBEq α] [LawfulHashable α] {k : α} (h : k ∈ m) : m.get k h == k := + ExtHashMap.getKey_beq h + +theorem get_congr [EquivBEq α] [LawfulHashable α] {k₁ k₂ : α} (h : k₁ == k₂) + (h₁ : k₁ ∈ m) : m.get k₁ h₁ = m.get k₂ ((mem_congr h).mp h₁) := + ExtHashMap.getKey_congr h h₁ + +theorem get_eq [LawfulBEq α] {k : α} (h : k ∈ m) : m.get k h = k := + ExtHashMap.getKey_eq h + +@[simp] +theorem get!_empty [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + (∅ : ExtHashSet α).get! a = default := + ExtHashMap.getKey!_empty + +theorem get!_insert [Inhabited α] [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.insert k).get! a = if k == a ∧ ¬k ∈ m then k else m.get! a := + ExtHashMap.getKey!_insertIfNew + +theorem get!_eq_default_of_contains_eq_false [Inhabited α] [EquivBEq α] [LawfulHashable α] {a : α} : + m.contains a = false → m.get! a = default := + ExtHashMap.getKey!_eq_default_of_contains_eq_false + +theorem get!_eq_default [Inhabited α] [EquivBEq α] [LawfulHashable α] {a : α} : + ¬a ∈ m → m.get! a = default := + ExtHashMap.getKey!_eq_default + +theorem get!_erase [Inhabited α] [EquivBEq α] [LawfulHashable α] {k a : α} : + (m.erase k).get! a = if k == a then default else m.get! a := + ExtHashMap.getKey!_erase + +@[simp] +theorem get!_erase_self [Inhabited α] [EquivBEq α] [LawfulHashable α] {k : α} : + (m.erase k).get! k = default := + ExtHashMap.getKey!_erase_self + +theorem get?_eq_some_get!_of_contains [EquivBEq α] [LawfulHashable α] [Inhabited α] + {a : α} : m.contains a = true → m.get? a = some (m.get! a) := + ExtHashMap.getKey?_eq_some_getKey!_of_contains + +theorem get?_eq_some_get! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + a ∈ m → m.get? a = some (m.get! a) := + ExtHashMap.getKey?_eq_some_getKey! + +theorem get!_eq_get!_get? [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.get! a = (m.get? a).get! := + ExtHashMap.getKey!_eq_get!_getKey? + +theorem get_eq_get! [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} {h'} : + m.get a h' = m.get! a := + ExtHashMap.getKey_eq_getKey! + +theorem get!_congr [EquivBEq α] [LawfulHashable α] [Inhabited α] {k k' : α} (h : k == k') : + m.get! k = m.get! k' := + ExtHashMap.getKey!_congr h + +theorem get!_eq_of_contains [LawfulBEq α] [Inhabited α] {k : α} (h : m.contains k) : m.get! k = k := + ExtHashMap.getKey!_eq_of_contains h + +theorem get!_eq_of_mem [LawfulBEq α] [Inhabited α] {k : α} (h : k ∈ m) : m.get! k = k := + ExtHashMap.getKey!_eq_of_mem h + +@[simp] +theorem getD_empty [EquivBEq α] [LawfulHashable α] {a fallback : α} : + (∅ : ExtHashSet α).getD a fallback = fallback := + ExtHashMap.getKeyD_empty + +theorem getD_insert [EquivBEq α] [LawfulHashable α] {k a fallback : α} : + (m.insert k).getD a fallback = if k == a ∧ ¬k ∈ m then k else m.getD a fallback := + ExtHashMap.getKeyD_insertIfNew + +theorem getD_eq_fallback_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {a fallback : α} : + m.contains a = false → m.getD a fallback = fallback := + ExtHashMap.getKeyD_eq_fallback_of_contains_eq_false + +theorem getD_eq_fallback [EquivBEq α] [LawfulHashable α] {a fallback : α} : + ¬a ∈ m → m.getD a fallback = fallback := + ExtHashMap.getKeyD_eq_fallback + +theorem getD_erase [EquivBEq α] [LawfulHashable α] {k a fallback : α} : + (m.erase k).getD a fallback = if k == a then fallback else m.getD a fallback := + ExtHashMap.getKeyD_erase + +@[simp] +theorem getD_erase_self [EquivBEq α] [LawfulHashable α] {k fallback : α} : + (m.erase k).getD k fallback = fallback := + ExtHashMap.getKeyD_erase_self + +theorem get?_eq_some_getD_of_contains [EquivBEq α] [LawfulHashable α] {a fallback : α} : + m.contains a = true → m.get? a = some (m.getD a fallback) := + ExtHashMap.getKey?_eq_some_getKeyD_of_contains + +theorem get?_eq_some_getD [EquivBEq α] [LawfulHashable α] {a fallback : α} : + a ∈ m → m.get? a = some (m.getD a fallback) := + ExtHashMap.getKey?_eq_some_getKeyD + +theorem getD_eq_getD_get? [EquivBEq α] [LawfulHashable α] {a fallback : α} : + m.getD a fallback = (m.get? a).getD fallback := + ExtHashMap.getKeyD_eq_getD_getKey? + +theorem get_eq_getD [EquivBEq α] [LawfulHashable α] {a fallback : α} {h'} : + m.get a h' = m.getD a fallback := + @ExtHashMap.getKey_eq_getKeyD _ _ _ _ _ _ _ _ _ h' + +theorem get!_eq_getD_default [EquivBEq α] [LawfulHashable α] [Inhabited α] {a : α} : + m.get! a = m.getD a default := + ExtHashMap.getKey!_eq_getKeyD_default + +theorem getD_congr [EquivBEq α] [LawfulHashable α] {k k' fallback : α} + (h : k == k') : m.getD k fallback = m.getD k' fallback := + ExtHashMap.getKeyD_congr h + +theorem getD_eq_of_contains [LawfulBEq α] {k fallback : α} (h : m.contains k) : + m.getD k fallback = k := + ExtHashMap.getKeyD_eq_of_contains h + +theorem getD_eq_of_mem [LawfulBEq α] {k fallback : α} (h : k ∈ m) : m.getD k fallback = k := + ExtHashMap.getKeyD_eq_of_mem h + +@[simp] +theorem containsThenInsert_fst [EquivBEq α] [LawfulHashable α] {k : α} : + (m.containsThenInsert k).1 = m.contains k := + ExtHashMap.containsThenInsertIfNew_fst + +@[simp] +theorem containsThenInsert_snd [EquivBEq α] [LawfulHashable α] {k : α} : + (m.containsThenInsert k).2 = m.insert k := + ext ExtHashMap.containsThenInsertIfNew_snd + +variable {ρ : Type v} [ForIn Id ρ α] + +@[simp] +theorem insertMany_nil [EquivBEq α] [LawfulHashable α] : + insertMany m [] = m := + ext ExtHashMap.insertManyIfNewUnit_nil + +@[simp] +theorem insertMany_list_singleton [EquivBEq α] [LawfulHashable α] {k : α} : + insertMany m [k] = m.insert k := + ext ExtHashMap.insertManyIfNewUnit_list_singleton + +theorem insertMany_cons [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : + insertMany m (k :: l) = insertMany (m.insert k) l := + ext ExtHashMap.insertManyIfNewUnit_cons + +@[elab_as_elim] +theorem insertMany_ind [EquivBEq α] [LawfulHashable α] + {motive : ExtHashSet α → Prop} (m : ExtHashSet α) {l : ρ} + (init : motive m) (insert : ∀ m a, motive m → motive (m.insert a)) : + motive (m.insertMany l) := + show motive ⟨m.1.insertManyIfNewUnit l⟩ from + ExtHashMap.insertManyIfNewUnit_ind m.inner l init fun m => insert ⟨m⟩ + +@[simp] +theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (insertMany m l).contains k = (m.contains k || l.contains k) := + ExtHashMap.contains_insertManyIfNewUnit_list + +@[simp] +theorem mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + k ∈ insertMany m l ↔ k ∈ m ∨ l.contains k := + ExtHashMap.mem_insertManyIfNewUnit_list + +theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (contains_eq_false : l.contains k = false) : + k ∈ insertMany m l → k ∈ m := + ExtHashMap.mem_of_mem_insertManyIfNewUnit_list contains_eq_false + +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} : k ∈ m → k ∈ m.insertMany l := + ExtHashMap.mem_insertManyIfNewUnit_of_mem + +theorem get?_insertMany_list_of_not_mem_of_contains_eq_false + [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + get? (insertMany m l) k = none := + ExtHashMap.getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + not_mem contains_eq_false + +theorem get?_insertMany_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + get? (insertMany m l) k' = some k := + ExtHashMap.getKey?_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem get?_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (mem : k ∈ m) : + get? (insertMany m l) k = get? m k := + ExtHashMap.getKey?_insertManyIfNewUnit_list_of_mem mem + +theorem get_insertMany_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} + {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) {h} : + get (insertMany m l) k' h = k := + ExtHashMap.getKey_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem get_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (mem : k ∈ m) {h} : + get (insertMany m l) k h = get m k mem := + ExtHashMap.getKey_insertManyIfNewUnit_list_of_mem mem + +theorem get!_insertMany_list_of_not_mem_of_contains_eq_false + [EquivBEq α] [LawfulHashable α] [Inhabited α] {l : List α} {k : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + get! (insertMany m l) k = default := + ExtHashMap.getKey!_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + not_mem contains_eq_false + +theorem get!_insertMany_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k k' : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + get! (insertMany m l) k' = k := + ExtHashMap.getKey!_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem get!_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} (mem : k ∈ m) : + get! (insertMany m l) k = get! m k := + ExtHashMap.getKey!_insertManyIfNewUnit_list_of_mem mem + +theorem getD_insertMany_list_of_not_mem_of_contains_eq_false + [EquivBEq α] [LawfulHashable α] {l : List α} {k fallback : α} + (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : + getD (insertMany m l) k fallback = fallback := + ExtHashMap.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false + not_mem contains_eq_false + +theorem getD_insertMany_list_of_not_mem_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' fallback : α} (k_beq : k == k') + (not_mem : ¬ k ∈ m) + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + getD (insertMany m l) k' fallback = k := + ExtHashMap.getKeyD_insertManyIfNewUnit_list_of_not_mem_of_mem + k_beq not_mem distinct mem + +theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} (mem : k ∈ m) : + getD (insertMany m l) k fallback = getD m k fallback := + ExtHashMap.getKeyD_insertManyIfNewUnit_list_of_mem mem + +theorem size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List α} + (distinct : l.Pairwise (fun a b => (a == b) = false)) : + (∀ (a : α), a ∈ m → l.contains a = false) → + (insertMany m l).size = m.size + l.length := + ExtHashMap.size_insertManyIfNewUnit_list distinct + +theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] + {l : List α} : + m.size ≤ (insertMany m l).size := + ExtHashMap.size_le_size_insertManyIfNewUnit_list + +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertMany m l).size := + ExtHashMap.size_le_size_insertManyIfNewUnit + +theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] + {l : List α} : + (insertMany m l).size ≤ m.size + l.length := + ExtHashMap.size_insertManyIfNewUnit_list_le + +@[simp] +theorem insertMany_list_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List α} : + m.insertMany l = ∅ ↔ m = ∅ ∧ l = [] := by + simpa only [ext_iff] using ExtHashMap.insertManyIfNewUnit_list_eq_empty_iff + +theorem eq_empty_of_insertMany_eq_empty [EquivBEq α] [LawfulHashable α] {l : ρ} : + m.insertMany l = ∅ → m = ∅ := by + simpa only [ext_iff] using ExtHashMap.eq_empty_of_insertManyIfNewUnit_eq_empty + +end + +section + +@[simp] +theorem ofList_nil [EquivBEq α] [LawfulHashable α] : + ofList ([] : List α) = ∅ := + ext ExtHashMap.unitOfList_nil + +@[simp] +theorem ofList_singleton [EquivBEq α] [LawfulHashable α] {k : α} : + ofList [k] = (∅ : ExtHashSet α).insert k := + ext ExtHashMap.unitOfList_singleton + +theorem ofList_cons [EquivBEq α] [LawfulHashable α] {hd : α} {tl : List α} : + ofList (hd :: tl) = + insertMany ((∅ : ExtHashSet α).insert hd) tl := + ext ExtHashMap.unitOfList_cons + +@[simp] +theorem contains_ofList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + (ofList l).contains k = l.contains k := + ExtHashMap.contains_unitOfList + +@[simp] +theorem mem_ofList [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} : + k ∈ ofList l ↔ l.contains k := + ExtHashMap.mem_unitOfList + +theorem get?_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k : α} (contains_eq_false : l.contains k = false) : + get? (ofList l) k = none := + ExtHashMap.getKey?_unitOfList_of_contains_eq_false contains_eq_false + +theorem get?_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) (mem : k ∈ l) : + get? (ofList l) k' = some k := + ExtHashMap.getKey?_unitOfList_of_mem k_beq distinct mem + +theorem get_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} + {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) {h} : + get (ofList l) k' h = k := + ExtHashMap.getKey_unitOfList_of_mem k_beq distinct mem + +theorem get!_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k : α} + (contains_eq_false : l.contains k = false) : + get! (ofList l) k = default := + ExtHashMap.getKey!_unitOfList_of_contains_eq_false contains_eq_false + +theorem get!_ofList_of_mem [EquivBEq α] [LawfulHashable α] + [Inhabited α] {l : List α} {k k' : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + get! (ofList l) k' = k := + ExtHashMap.getKey!_unitOfList_of_mem k_beq distinct mem + +theorem getD_ofList_of_contains_eq_false [EquivBEq α] [LawfulHashable α] + {l : List α} {k fallback : α} + (contains_eq_false : l.contains k = false) : + getD (ofList l) k fallback = fallback := + ExtHashMap.getKeyD_unitOfList_of_contains_eq_false contains_eq_false + +theorem getD_ofList_of_mem [EquivBEq α] [LawfulHashable α] + {l : List α} {k k' fallback : α} (k_beq : k == k') + (distinct : l.Pairwise (fun a b => (a == b) = false)) + (mem : k ∈ l) : + getD (ofList l) k' fallback = k := + ExtHashMap.getKeyD_unitOfList_of_mem k_beq distinct mem + +theorem size_ofList [EquivBEq α] [LawfulHashable α] + {l : List α} + (distinct : l.Pairwise (fun a b => (a == b) = false)) : + (ofList l).size = l.length := + ExtHashMap.size_unitOfList distinct + +theorem size_ofList_le [EquivBEq α] [LawfulHashable α] + {l : List α} : + (ofList l).size ≤ l.length := + ExtHashMap.size_unitOfList_le + +@[simp] +theorem ofList_eq_empty_iff [EquivBEq α] [LawfulHashable α] {l : List α} : + ofList l = ∅ ↔ l = [] := + ext_iff.trans ExtHashMap.unitOfList_eq_empty_iff + +end + +section Ext + +@[ext 900] +theorem ext_get? [EquivBEq α] [LawfulHashable α] {m₁ m₂ : ExtHashSet α} + (h : ∀ k, m₁.get? k = m₂.get? k) : m₁ = m₂ := + ext (ExtHashMap.ext_getKey?_unit h) + +theorem ext_contains [LawfulBEq α] {m₁ m₂ : ExtHashSet α} + (h : ∀ k, m₁.contains k = m₂.contains k) : m₁ = m₂ := + ext (ExtHashMap.ext_contains_unit h) + +@[ext] +theorem ext_mem [LawfulBEq α] {m₁ m₂ : ExtHashSet α} (h : ∀ k, k ∈ m₁ ↔ k ∈ m₂) : m₁ = m₂ := + ext (ExtHashMap.ext_mem_unit h) + +end Ext + +section filter + +variable {m : ExtHashSet α} + +theorem filter_eq_empty_iff [EquivBEq α] [LawfulHashable α] {f : α → Bool} : + m.filter f = ∅ ↔ ∀ k h, f (m.get k h) = false := + ext_iff.trans ExtHashMap.filter_eq_empty_iff + +@[simp] +theorem mem_filter [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} : + k ∈ m.filter f ↔ ∃ h, f (m.get k h) := + ExtHashMap.mem_filter + +theorem contains_of_contains_filter [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} : + (m.filter f).contains k → m.contains k := + ExtHashMap.contains_of_contains_filter + +theorem mem_of_mem_filter [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} : + k ∈ m.filter f → k ∈ m := + ExtHashMap.mem_of_mem_filter + +theorem size_filter_le_size [EquivBEq α] [LawfulHashable α] + {f : α → Bool} : + (m.filter f).size ≤ m.size := + ExtHashMap.size_filter_le_size + +theorem size_filter_eq_size_iff [EquivBEq α] [LawfulHashable α] + {f : α → Bool} : + (m.filter f).size = m.size ↔ ∀ k h, f (m.get k h) := + ExtHashMap.size_filter_eq_size_iff + +theorem filter_eq_self_iff [EquivBEq α] [LawfulHashable α] + {f : α → Bool} : + m.filter f = m ↔ ∀ k h, f (m.get k h) := + ext_iff.trans ExtHashMap.filter_eq_self_iff + +@[simp] +theorem get?_filter [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} : + (m.filter f).get? k = (m.get? k).filter f := + ExtHashMap.getKey?_filter_key + +@[simp] +theorem get_filter [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k : α} {h} : + (m.filter f).get k h = m.get k (mem_of_mem_filter h) := + ExtHashMap.getKey_filter + +theorem get!_filter [EquivBEq α] [LawfulHashable α] [Inhabited α] + {f : α → Bool} {k : α} : + (m.filter f).get! k = ((m.get? k).filter f).get! := + ExtHashMap.getKey!_filter_key + +theorem getD_filter [EquivBEq α] [LawfulHashable α] + {f : α → Bool} {k fallback : α} : + (m.filter f).getD k fallback = ((m.get? k).filter f).getD fallback := + ExtHashMap.getKeyD_filter_key + +end filter + +end Std.ExtHashSet diff --git a/src/Std/Data/HashMap/Basic.lean b/src/Std/Data/HashMap/Basic.lean index 6101ea4c38..073437e067 100644 --- a/src/Std/Data/HashMap/Basic.lean +++ b/src/Std/Data/HashMap/Basic.lean @@ -12,13 +12,13 @@ set_option autoImplicit false /-! # Hash maps -This module develops the type `Std.Data.HashMap` of hash maps. Dependent hash maps are defined in +This module develops the type `Std.HashMap` of hash maps. Dependent hash maps are defined in `Std.Data.DHashMap`. -The operations `map` and `filterMap` on `Std.Data.HashMap` are defined in the module +The operations `map` and `filterMap` on `Std.HashMap` are defined in the module `Std.Data.HashMap.AdditionalOperations`. -Lemmas about the operations on `Std.Data.HashMap` are available in the +Lemmas about the operations on `Std.HashMap` are available in the module `Std.Data.HashMap.Lemmas`. See the module `Std.Data.HashMap.Raw` for a variant of this type which is safe to use in diff --git a/src/Std/Data/HashMap/Lemmas.lean b/src/Std/Data/HashMap/Lemmas.lean index 127fee1603..c02e63da60 100644 --- a/src/Std/Data/HashMap/Lemmas.lean +++ b/src/Std/Data/HashMap/Lemmas.lean @@ -935,6 +935,8 @@ theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m'] end monadic +variable {ρ : Type w} [ForIn Id ρ (α × β)] + @[simp] theorem insertMany_nil : insertMany m [] = m := @@ -949,6 +951,13 @@ theorem insertMany_cons {l : List (α × β)} {k : α} {v : β} : insertMany m (⟨k, v⟩ :: l) = insertMany (m.insert k v) l := ext DHashMap.Const.insertMany_cons +@[elab_as_elim] +theorem insertMany_ind {motive : HashMap α β → Prop} (m : HashMap α β) {l : ρ} + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l) := + show motive ⟨DHashMap.Const.insertMany m.1 l⟩ from + DHashMap.Const.insertMany_ind m.inner l init fun m => insert ⟨m⟩ + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {k : α} : @@ -967,6 +976,10 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] k ∈ m := DHashMap.Const.mem_of_mem_insertMany_list mem contains_eq_false +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} : k ∈ m → k ∈ m.insertMany l := + DHashMap.Const.mem_insertMany_of_mem + theorem getElem?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1087,6 +1100,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertMany m l).size := DHashMap.Const.size_le_size_insertMany_list +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertMany m l).size := + DHashMap.Const.size_le_size_insertMany + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] {l : List (α × β)} : (insertMany m l).size ≤ m.size + l.length := @@ -1098,7 +1115,12 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (insertMany m l).isEmpty = (m.isEmpty && l.isEmpty) := DHashMap.Const.isEmpty_insertMany_list +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : (insertMany m l).isEmpty → m.isEmpty := + DHashMap.Const.isEmpty_of_isEmpty_insertMany + variable {m : HashMap α Unit} +variable {ρ : Type w} [ForIn Id ρ α] @[simp] theorem insertManyIfNewUnit_nil : @@ -1114,6 +1136,13 @@ theorem insertManyIfNewUnit_cons {l : List α} {k : α} : insertManyIfNewUnit m (k :: l) = insertManyIfNewUnit (m.insertIfNew k ()) l := ext DHashMap.Const.insertManyIfNewUnit_cons +@[elab_as_elim] +theorem insertManyIfNewUnit_ind {motive : HashMap α Unit → Prop} (m : HashMap α Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l) := + show motive ⟨DHashMap.Const.insertManyIfNewUnit m.1 l⟩ from + DHashMap.Const.insertManyIfNewUnit_ind m.inner l init fun m => insert ⟨m⟩ + @[simp] theorem contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : @@ -1131,6 +1160,10 @@ theorem mem_of_mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] k ∈ insertManyIfNewUnit m l → k ∈ m := DHashMap.Const.mem_of_mem_insertManyIfNewUnit_list contains_eq_false +theorem mem_insertManyIfNewUnit_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} : k ∈ m → k ∈ insertManyIfNewUnit m l := + DHashMap.Const.mem_insertManyIfNewUnit_of_mem + theorem getElem?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : (insertManyIfNewUnit m l)[k]? = @@ -1235,6 +1268,10 @@ theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertManyIfNewUnit m l).size := DHashMap.Const.size_le_size_insertManyIfNewUnit_list +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertManyIfNewUnit m l).size := + DHashMap.Const.size_le_size_insertManyIfNewUnit + theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] {l : List α} : (insertManyIfNewUnit m l).size ≤ m.size + l.length := @@ -1246,6 +1283,10 @@ theorem isEmpty_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (insertManyIfNewUnit m l).isEmpty = (m.isEmpty && l.isEmpty) := DHashMap.Const.isEmpty_insertManyIfNewUnit_list +theorem isEmpty_of_isEmpty_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] + {l : ρ} : (m.insertManyIfNewUnit l).isEmpty → m.isEmpty := + DHashMap.Const.isEmpty_of_isEmpty_insertManyIfNewUnit + end section @@ -1937,12 +1978,15 @@ namespace Equiv variable {m m₁ m₂ m₃ : HashMap α β} -theorem refl (m : HashMap α β) : m ~m m := ⟨.rfl⟩ +@[refl, simp] theorem refl (m : HashMap α β) : m ~m m := ⟨.rfl⟩ theorem rfl : m ~m m := ⟨.rfl⟩ -theorem symm : m₁ ~m m₂ → m₂ ~m m₁ +@[symm] theorem symm : m₁ ~m m₂ → m₂ ~m m₁ | ⟨h⟩ => ⟨h.symm⟩ theorem trans : m₁ ~m m₂ → m₂ ~m m₃ → m₁ ~m m₃ | ⟨h₁⟩, ⟨h₂⟩ => ⟨h₁.trans h₂⟩ + +instance instTrans : Trans (α := HashMap α β) Equiv Equiv Equiv := ⟨trans⟩ + theorem comm : m₁ ~m m₂ ↔ m₂ ~m m₁ := ⟨symm, symm⟩ theorem congr_left (h : m₁ ~m m₂) : m₁ ~m m₃ ↔ m₂ ~m m₃ := ⟨h.symm.trans, h.trans⟩ theorem congr_right (h : m₁ ~m m₂) : m₃ ~m m₁ ↔ m₃ ~m m₂ := @@ -2043,15 +2087,20 @@ theorem map (f : α → β → γ) (h : m₁ ~m m₂) : m₁.map f ~m m₂.map f theorem filterMap (f : α → β → Option γ) (h : m₁ ~m m₂) : m₁.filterMap f ~m m₂.filterMap f := ⟨h.1.filterMap f⟩ +theorem of_forall_getKey_eq_of_forall_getElem?_eq [EquivBEq α] [LawfulHashable α] + (hk : ∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') (hv : ∀ k : α, m₁[k]? = m₂[k]?) : + m₁ ~m m₂ := + ⟨.of_forall_getKey_eq_of_forall_constGet?_eq hk hv⟩ + +set_option linter.deprecated false in +@[deprecated of_forall_getKey_eq_of_forall_getElem?_eq (since := "2025-04-25")] theorem of_forall_getKey?_eq_of_forall_getElem?_eq [EquivBEq α] [LawfulHashable α] (hk : ∀ k, m₁.getKey? k = m₂.getKey? k) (hv : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ ~m m₂ := ⟨.of_forall_getKey?_eq_of_forall_constGet?_eq hk hv⟩ theorem of_forall_getElem?_eq [LawfulBEq α] (h : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ ~m m₂ := - ⟨.of_forall_get?_eq fun k => - DHashMap.Const.get?_eq_get? (m := m₁.1) ▸ - DHashMap.Const.get?_eq_get? (m := m₂.1) ▸ h k⟩ + ⟨.of_forall_constGet?_eq h⟩ theorem of_forall_getKey?_unit_eq [EquivBEq α] [LawfulHashable α] {m₁ m₂ : HashMap α Unit} (h : ∀ k, m₁.getKey? k = m₂.getKey? k) : m₁ ~m m₂ := diff --git a/src/Std/Data/HashMap/Raw.lean b/src/Std/Data/HashMap/Raw.lean index 17afb02cf6..dfad43e1f8 100644 --- a/src/Std/Data/HashMap/Raw.lean +++ b/src/Std/Data/HashMap/Raw.lean @@ -9,17 +9,17 @@ import Std.Data.DHashMap.Raw set_option linter.missingDocs true set_option autoImplicit false -/- +/-! # Hash maps with unbundled well-formedness invariant -This module develops the type `Std.Data.HashMap.Raw` of dependent hash maps with unbundled +This module develops the type `Std.HashMap.Raw` of dependent hash maps with unbundled well-formedness invariant. This version is safe to use in nested inductive types. The well-formedness predicate is -available as `Std.Data.HashMap.Raw.WF` and we prove in this file that all operations preserve -well-formedness. When in doubt, prefer `HashMap` over `DHashMap.Raw`. +available as `Std.HashMap.Raw.WF` and we prove in this file that all operations preserve +well-formedness. When in doubt, prefer `HashMap` over `HashMap.Raw`. -Lemmas about the operations on `Std.Data.HashMap.Raw` are available in the module +Lemmas about the operations on `Std.HashMap.Raw` are available in the module `Std.Data.HashMap.RawLemmas`. -/ diff --git a/src/Std/Data/HashMap/RawLemmas.lean b/src/Std/Data/HashMap/RawLemmas.lean index 6182bee378..0b3157b415 100644 --- a/src/Std/Data/HashMap/RawLemmas.lean +++ b/src/Std/Data/HashMap/RawLemmas.lean @@ -958,6 +958,8 @@ theorem forIn_eq_forIn_keys [Monad m'] [LawfulMonad m'] (h : m.WF) end monadic +variable {ρ : Type w} [ForIn Id ρ (α × β)] + @[simp] theorem insertMany_nil (h : m.WF) : insertMany m [] = m := @@ -974,6 +976,13 @@ theorem insertMany_cons (h : m.WF) {l : List (α × β)} insertMany m (⟨k, v⟩ :: l) = insertMany (m.insert k v) l := ext (DHashMap.Raw.Const.insertMany_cons h.out) +@[elab_as_elim] +theorem insertMany_ind {motive : Raw α β → Prop} (m : Raw α β) {l : ρ} + (init : motive m) (insert : ∀ m a b, motive m → motive (m.insert a b)) : + motive (m.insertMany l) := + show motive ⟨DHashMap.Raw.Const.insertMany m.1 l⟩ from + DHashMap.Raw.Const.insertMany_ind m.inner l init fun m => insert ⟨m⟩ + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} {k : α} : @@ -991,6 +1000,10 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) k ∈ insertMany m l → (l.map Prod.fst).contains k = false → k ∈ m := DHashMap.Raw.Const.mem_of_mem_insertMany_list h.out +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} {k : α} : k ∈ m → k ∈ m.insertMany l := + DHashMap.Raw.Const.mem_insertMany_of_mem h.out + theorem getKey?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1062,6 +1075,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertMany m l).size := DHashMap.Raw.Const.size_le_size_insertMany_list h.out +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : m.size ≤ (insertMany m l).size := + DHashMap.Raw.Const.size_le_size_insertMany h.out + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} : (insertMany m l).size ≤ m.size + l.length := @@ -1073,6 +1090,10 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (insertMany m l).isEmpty = (m.isEmpty && l.isEmpty) := DHashMap.Raw.Const.isEmpty_insertMany_list h.out +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : (insertMany m l).isEmpty → m.isEmpty := + DHashMap.Raw.Const.isEmpty_of_isEmpty_insertMany h.out + theorem getElem?_insertMany_list_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List (α × β)} {k : α} (contains_eq_false : (l.map Prod.fst).contains k = false) : @@ -1124,6 +1145,7 @@ theorem getD_insertMany_list_of_mem [EquivBEq α] [LawfulHashable α] DHashMap.Raw.Const.getD_insertMany_list_of_mem h.out k_beq distinct mem variable {m : Raw α Unit} +variable {ρ : Type w} [ForIn Id ρ α] @[simp] theorem insertManyIfNewUnit_nil (h : m.WF) : @@ -1139,6 +1161,13 @@ theorem insertManyIfNewUnit_cons (h : m.WF) {l : List α} {k : α} : insertManyIfNewUnit m (k :: l) = insertManyIfNewUnit (m.insertIfNew k ()) l := ext (DHashMap.Raw.Const.insertManyIfNewUnit_cons h.out) +@[elab_as_elim] +theorem insertManyIfNewUnit_ind {motive : Raw α Unit → Prop} (m : Raw α Unit) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insertIfNew a ())) : + motive (insertManyIfNewUnit m l) := + show motive ⟨DHashMap.Raw.Const.insertManyIfNewUnit m.1 l⟩ from + DHashMap.Raw.Const.insertManyIfNewUnit_ind m.inner l init fun m => insert ⟨m⟩ + @[simp] theorem contains_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} : @@ -1156,6 +1185,10 @@ theorem mem_of_mem_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h k ∈ insertManyIfNewUnit m l → k ∈ m := DHashMap.Raw.Const.mem_of_mem_insertManyIfNewUnit_list h.out contains_eq_false +theorem mem_insertManyIfNewUnit_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} {k : α} : k ∈ m → k ∈ insertManyIfNewUnit m l := + DHashMap.Raw.Const.mem_insertManyIfNewUnit_of_mem h.out + theorem getKey?_insertManyIfNewUnit_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : @@ -1242,6 +1275,10 @@ theorem size_le_size_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertManyIfNewUnit m l).size := DHashMap.Raw.Const.size_le_size_insertManyIfNewUnit_list h.out +theorem size_le_size_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : m.size ≤ (insertManyIfNewUnit m l).size := + DHashMap.Raw.Const.size_le_size_insertManyIfNewUnit h.out + theorem size_insertManyIfNewUnit_list_le [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} : (insertManyIfNewUnit m l).size ≤ m.size + l.length := @@ -1253,6 +1290,10 @@ theorem isEmpty_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : (insertManyIfNewUnit m l).isEmpty = (m.isEmpty && l.isEmpty) := DHashMap.Raw.Const.isEmpty_insertManyIfNewUnit_list h.out +theorem isEmpty_of_isEmpty_insertManyIfNewUnit [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : (m.insertManyIfNewUnit l).isEmpty → m.isEmpty := + DHashMap.Raw.Const.isEmpty_of_isEmpty_insertManyIfNewUnit h.out + @[simp] theorem getElem?_insertManyIfNewUnit_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} : @@ -1969,12 +2010,15 @@ section Raw variable {α : Type u} {β : Type v} {m m₁ m₂ m₃ : Raw α β} -theorem refl (m : Raw α β) : m ~m m := ⟨.rfl⟩ +@[refl, simp] theorem refl (m : Raw α β) : m ~m m := ⟨.rfl⟩ theorem rfl : m ~m m := ⟨.rfl⟩ -theorem symm : m₁ ~m m₂ → m₂ ~m m₁ +@[symm] theorem symm : m₁ ~m m₂ → m₂ ~m m₁ | ⟨h⟩ => ⟨h.symm⟩ theorem trans : m₁ ~m m₂ → m₂ ~m m₃ → m₁ ~m m₃ | ⟨h₁⟩, ⟨h₂⟩ => ⟨h₁.trans h₂⟩ + +instance instTrans : Trans (α := Raw α β) Equiv Equiv Equiv := ⟨trans⟩ + theorem comm : m₁ ~m m₂ ↔ m₂ ~m m₁ := ⟨symm, symm⟩ theorem congr_left (h : m₁ ~m m₂) : m₁ ~m m₃ ↔ m₂ ~m m₃ := ⟨h.symm.trans, h.trans⟩ theorem congr_right (h : m₁ ~m m₂) : m₃ ~m m₁ ↔ m₃ ~m m₂ := @@ -2086,6 +2130,13 @@ theorem filterMap (h₁ : m₁.WF) (h₂ : m₂.WF) (f : α → β → Option γ m₁.filterMap f ~m m₂.filterMap f := ⟨h.1.filterMap h₁.1 h₂.1 f⟩ +theorem of_forall_getKey_eq_of_forall_getElem?_eq [EquivBEq α] [LawfulHashable α] + (h₁ : m₁.WF) (h₂ : m₂.WF) (hk : ∀ k hk hk', m₁.getKey k hk = m₂.getKey k hk') + (hv : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ ~m m₂ := + ⟨.of_forall_getKey_eq_of_forall_constGet?_eq h₁.1 h₂.1 hk hv⟩ + +set_option linter.deprecated false in +@[deprecated of_forall_getKey_eq_of_forall_getElem?_eq (since := "2025-04-25")] theorem of_forall_getKey?_eq_of_forall_getElem?_eq [EquivBEq α] [LawfulHashable α] (h₁ : m₁.WF) (h₂ : m₂.WF) (hk : ∀ k, m₁.getKey? k = m₂.getKey? k) (hv : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ ~m m₂ := @@ -2093,9 +2144,7 @@ theorem of_forall_getKey?_eq_of_forall_getElem?_eq [EquivBEq α] [LawfulHashable theorem of_forall_getElem?_eq [LawfulBEq α] (h₁ : m₁.WF) (h₂ : m₂.WF) (h : ∀ k : α, m₁[k]? = m₂[k]?) : m₁ ~m m₂ := - ⟨.of_forall_get?_eq h₁.1 h₂.1 fun k => - DHashMap.Raw.Const.get?_eq_get? h₁.1 ▸ - DHashMap.Raw.Const.get?_eq_get? h₂.1 ▸ h k⟩ + ⟨.of_forall_constGet?_eq h₁.1 h₂.1 h⟩ theorem of_forall_getKey?_unit_eq [EquivBEq α] [LawfulHashable α] {m₁ m₂ : HashMap.Raw α Unit} (h₁ : m₁.WF) (h₂ : m₂.WF) @@ -2112,7 +2161,6 @@ theorem of_forall_mem_unit_iff [LawfulBEq α] (h : ∀ k, k ∈ m₁ ↔ k ∈ m₂) : m₁ ~m m₂ := ⟨.of_forall_mem_unit_iff h₁.1 h₂.1 h⟩ - end Equiv theorem equiv_emptyWithCapacity_iff_isEmpty [EquivBEq α] [LawfulHashable α] {c : Nat} (h : m.WF) : diff --git a/src/Std/Data/HashSet/Basic.lean b/src/Std/Data/HashSet/Basic.lean index a93362739d..e3e495e679 100644 --- a/src/Std/Data/HashSet/Basic.lean +++ b/src/Std/Data/HashSet/Basic.lean @@ -9,9 +9,9 @@ import Std.Data.HashMap.Basic /-! # Hash sets -This module develops the type `Std.Data.HashSet` of dependent hash sets. +This module develops the type `Std.HashSet` of hash sets. -Lemmas about the operations on `Std.Data.HashSet` are available in the +Lemmas about the operations on `Std.HashSet` are available in the module `Std.Data.HashSet.Lemmas`. See the module `Std.Data.HashSet.Raw` for a variant of this type which is safe to use in diff --git a/src/Std/Data/HashSet/Lemmas.lean b/src/Std/Data/HashSet/Lemmas.lean index 02c47970d2..51fb5917e4 100644 --- a/src/Std/Data/HashSet/Lemmas.lean +++ b/src/Std/Data/HashSet/Lemmas.lean @@ -496,6 +496,8 @@ theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m'] end monadic +variable {ρ : Type v} [ForIn Id ρ α] + @[simp] theorem insertMany_nil : insertMany m [] = m := @@ -510,6 +512,13 @@ theorem insertMany_cons {l : List α} {k : α} : insertMany m (k :: l) = insertMany (m.insert k) l := ext HashMap.insertManyIfNewUnit_cons +@[elab_as_elim] +theorem insertMany_ind {motive : HashSet α → Prop} (m : HashSet α) {l : ρ} + (init : motive m) (insert : ∀ m a, motive m → motive (m.insert a)) : + motive (m.insertMany l) := + show motive ⟨m.1.insertManyIfNewUnit l⟩ from + HashMap.insertManyIfNewUnit_ind m.inner l init fun m => insert ⟨m⟩ + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} : @@ -527,6 +536,10 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] k ∈ insertMany m l → k ∈ m := HashMap.mem_of_mem_insertManyIfNewUnit_list contains_eq_false +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] + {l : ρ} {k : α} : k ∈ m → k ∈ m.insertMany l := + HashMap.mem_insertManyIfNewUnit_of_mem + theorem get?_insertMany_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] {l : List α} {k : α} (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : @@ -613,6 +626,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] m.size ≤ (insertMany m l).size := HashMap.size_le_size_insertManyIfNewUnit_list +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : m.size ≤ (insertMany m l).size := + HashMap.size_le_size_insertManyIfNewUnit + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] {l : List α} : (insertMany m l).size ≤ m.size + l.length := @@ -624,6 +641,10 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (insertMany m l).isEmpty = (m.isEmpty && l.isEmpty) := HashMap.isEmpty_insertManyIfNewUnit_list +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] + {l : ρ} : (insertMany m l).isEmpty → m.isEmpty := + HashMap.isEmpty_of_isEmpty_insertManyIfNewUnit + end section @@ -723,12 +744,15 @@ namespace Equiv variable {m m₁ m₂ m₃ : HashSet α} -theorem refl (m : HashSet α) : m ~m m := ⟨.rfl⟩ +@[refl, simp] theorem refl (m : HashSet α) : m ~m m := ⟨.rfl⟩ theorem rfl : m ~m m := ⟨.rfl⟩ -theorem symm : m₁ ~m m₂ → m₂ ~m m₁ +@[symm] theorem symm : m₁ ~m m₂ → m₂ ~m m₁ | ⟨h⟩ => ⟨h.symm⟩ theorem trans : m₁ ~m m₂ → m₂ ~m m₃ → m₁ ~m m₃ | ⟨h₁⟩, ⟨h₂⟩ => ⟨h₁.trans h₂⟩ + +instance instTrans : Trans (α := HashSet α) Equiv Equiv Equiv := ⟨trans⟩ + theorem comm : m₁ ~m m₂ ↔ m₂ ~m m₁ := ⟨symm, symm⟩ theorem congr_left (h : m₁ ~m m₂) : m₁ ~m m₃ ↔ m₂ ~m m₃ := ⟨h.symm.trans, h.trans⟩ theorem congr_right (h : m₁ ~m m₂) : m₃ ~m m₁ ↔ m₃ ~m m₂ := diff --git a/src/Std/Data/HashSet/Raw.lean b/src/Std/Data/HashSet/Raw.lean index 9d69422796..7b2914dd29 100644 --- a/src/Std/Data/HashSet/Raw.lean +++ b/src/Std/Data/HashSet/Raw.lean @@ -6,17 +6,17 @@ Authors: Markus Himmel prelude import Std.Data.HashMap.Raw -/- +/-! # Hash sets with unbundled well-formedness invariant -This module develops the type `Std.Data.HashSet.Raw` of dependent hash -set with unbundled well-formedness invariant. +This module develops the type `Std.HashSet.Raw` of hash sets with +unbundled well-formedness invariant. This version is safe to use in nested inductive types. The well-formedness predicate is -available as `Std.Data.HashSet.Raw.WF` and we prove in this file that all operations preserve +available as `Std.HashSet.Raw.WF` and we prove in this file that all operations preserve well-formedness. When in doubt, prefer `HashSet` over `HashSet.Raw`. -Lemmas about the operations on `Std.Data.HashSet.Raw` are available in the module +Lemmas about the operations on `Std.HashSet.Raw` are available in the module `Std.Data.HashSet.RawLemmas`. -/ diff --git a/src/Std/Data/HashSet/RawLemmas.lean b/src/Std/Data/HashSet/RawLemmas.lean index 57005af7a3..a220b40a56 100644 --- a/src/Std/Data/HashSet/RawLemmas.lean +++ b/src/Std/Data/HashSet/RawLemmas.lean @@ -521,6 +521,8 @@ theorem forIn_eq_forIn_toList [Monad m'] [LawfulMonad m'] (h : m.WF) end monadic +variable {ρ : Type v} [ForIn Id ρ α] + @[simp] theorem insertMany_nil (h : m.WF) : insertMany m [] = m := @@ -535,6 +537,13 @@ theorem insertMany_cons (h : m.WF) {l : List α} {k : α} : insertMany m (k :: l) = insertMany (m.insert k) l := ext (HashMap.Raw.insertManyIfNewUnit_cons h.1) +@[elab_as_elim] +theorem insertMany_ind {motive : Raw α → Prop} (m : Raw α) (l : ρ) + (init : motive m) (insert : ∀ m a, motive m → motive (m.insert a)) : + motive (insertMany m l) := + show motive ⟨m.1.insertManyIfNewUnit l⟩ from + HashMap.Raw.insertManyIfNewUnit_ind m.inner l init fun m => insert ⟨m⟩ + @[simp] theorem contains_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} : @@ -552,6 +561,10 @@ theorem mem_of_mem_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) k ∈ insertMany m l → k ∈ m := HashMap.Raw.mem_of_mem_insertManyIfNewUnit_list h.1 contains_eq_false +theorem mem_insertMany_of_mem [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} {k : α} : k ∈ m → k ∈ insertMany m l := + HashMap.Raw.mem_insertManyIfNewUnit_of_mem h.out + theorem get?_insertMany_list_of_not_mem_of_contains_eq_false [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} {k : α} (not_mem : ¬ k ∈ m) (contains_eq_false : l.contains k = false) : @@ -638,6 +651,10 @@ theorem size_le_size_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF m.size ≤ (insertMany m l).size := HashMap.Raw.size_le_size_insertManyIfNewUnit_list h.1 +theorem size_le_size_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : m.size ≤ (insertMany m l).size := + HashMap.Raw.size_le_size_insertManyIfNewUnit h.out + theorem size_insertMany_list_le [EquivBEq α] [LawfulHashable α] (h : m.WF) {l : List α} : (insertMany m l).size ≤ m.size + l.length := @@ -649,6 +666,10 @@ theorem isEmpty_insertMany_list [EquivBEq α] [LawfulHashable α] (h : m.WF) (insertMany m l).isEmpty = (m.isEmpty && l.isEmpty) := HashMap.Raw.isEmpty_insertManyIfNewUnit_list h.1 +theorem isEmpty_of_isEmpty_insertMany [EquivBEq α] [LawfulHashable α] (h : m.WF) + {l : ρ} : (insertMany m l).isEmpty → m.isEmpty := + HashMap.Raw.isEmpty_of_isEmpty_insertManyIfNewUnit h.out + @[simp] theorem ofList_nil : ofList ([] : List α) = ∅ := @@ -744,12 +765,15 @@ section Raw variable {α : Type u} {β : Type v} {m m₁ m₂ m₃ : Raw α} -theorem refl (m : Raw α) : m ~m m := ⟨.rfl⟩ +@[refl, simp] theorem refl (m : Raw α) : m ~m m := ⟨.rfl⟩ theorem rfl : m ~m m := ⟨.rfl⟩ -theorem symm : m₁ ~m m₂ → m₂ ~m m₁ +@[symm] theorem symm : m₁ ~m m₂ → m₂ ~m m₁ | ⟨h⟩ => ⟨h.symm⟩ theorem trans : m₁ ~m m₂ → m₂ ~m m₃ → m₁ ~m m₃ | ⟨h₁⟩, ⟨h₂⟩ => ⟨h₁.trans h₂⟩ + +instance instTrans : Trans (α := Raw α) Equiv Equiv Equiv := ⟨trans⟩ + theorem comm : m₁ ~m m₂ ↔ m₂ ~m m₁ := ⟨symm, symm⟩ theorem congr_left (h : m₁ ~m m₂) : m₁ ~m m₃ ↔ m₂ ~m m₃ := ⟨h.symm.trans, h.trans⟩ theorem congr_right (h : m₁ ~m m₂) : m₃ ~m m₁ ↔ m₃ ~m m₂ := diff --git a/src/Std/Data/Internal/List/Associative.lean b/src/Std/Data/Internal/List/Associative.lean index 21bbc749a6..af8dab8e8c 100644 --- a/src/Std/Data/Internal/List/Associative.lean +++ b/src/Std/Data/Internal/List/Associative.lean @@ -2229,24 +2229,32 @@ theorem getValueCast?_ext [BEq α] [LawfulBEq α] {l l' : List ((a : α) × β a intro a simp only [getEntry?_eq_getValueCast?, h] -theorem getKey?_getValue?_ext [BEq α] [EquivBEq α] {β : Type v} +theorem getKey_getValue?_ext [BEq α] [EquivBEq α] {β : Type v} {l l' : List ((_ : α) × β)} (hl : DistinctKeys l) (hl' : DistinctKeys l') - (hk : ∀ a, getKey? a l = getKey? a l') (hv : ∀ a, getValue? a l = getValue? a l') : + (hk : ∀ a h h', getKey a l h = getKey a l' h') (hv : ∀ a, getValue? a l = getValue? a l') : Perm l l' := by apply getEntry?_ext hl hl' intro a specialize hk a; specialize hv a by_cases h' : containsKey a l' - · simp only [getKey?_eq_some_getKey h'] at hk - have h'' := containsKey_eq_isSome_getKey?.trans (hk ▸ rfl : (getKey? a l).isSome = true) - simp only [getKey?_eq_some_getKey, getValue?_eq_some_getValue, - getEntry?_eq_some_getEntry, h', h'', Option.some.injEq, - getEntry_eq_getKey_getValue, Sigma.mk.injEq] at hk hv ⊢ - exact ⟨hk, hv ▸ .rfl⟩ - · simp only [getKey?_eq_none, h'] at hk - have h'' := containsKey_eq_isSome_getKey?.trans (hk ▸ rfl : (getKey? a l).isSome = false) + · simp only [getValue?_eq_some_getValue h'] at hv + have h'' := containsKey_eq_isSome_getValue?.trans (hv ▸ rfl : (getValue? a l).isSome = true) + specialize hk h'' h' + simp only [getEntry?_eq_some_getEntry, h', h'', getEntry_eq_getKey_getValue, Sigma.mk.injEq] + simp only [getValue?_eq_some_getValue h'', Option.some.injEq] at hv + rw [hk, hv] + · simp only [getValue?_eq_none.mpr, h'] at hv + have h'' := containsKey_eq_isSome_getValue?.trans (hv ▸ rfl : (getValue? a l).isSome = false) simp only [getEntry?_eq_none.mpr, h', h''] +theorem getKey?_getValue?_ext [BEq α] [EquivBEq α] {β : Type v} + {l l' : List ((_ : α) × β)} (hl : DistinctKeys l) (hl' : DistinctKeys l') + (hk : ∀ a, getKey? a l = getKey? a l') (hv : ∀ a, getValue? a l = getValue? a l') : + Perm l l' := by + refine getKey_getValue?_ext hl hl' ?_ hv + intro a ha ha' + simp only [getKey, hk] + theorem getKey?_ext [BEq α] [EquivBEq α] {l l' : List ((_ : α) × Unit)} (hl : DistinctKeys l) (hl' : DistinctKeys l') (h : ∀ a, getKey? a l = getKey? a l') : Perm l l' := by