lean4-htt/tests/elab/grind_indexmap.lean
Garmelon 08eb78a5b2
chore: switch to new test/bench suite (#12590)
This PR sets up the new integrated test/bench suite. It then migrates
all benchmarks and some related tests to the new suite. There's also
some documentation and some linting.

For now, a lot of the old tests are left alone so this PR doesn't become
even larger than it already is. Eventually, all tests should be migrated
to the new suite though so there isn't a confusing mix of two systems.
2026-02-25 13:51:53 +00:00

197 lines
6.5 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module
-- See also the companion file `grind_indexmap_pre.lean`,
-- showing this file might have looked like before any proofs are written.
-- This file fills them all in with `grind`!
import Std.Data.HashMap
import Lean.LibrarySuggestions.Default
local macro_rules | `(tactic| get_elem_tactic_extensible) => `(tactic| grind)
open Std
/--
An `IndexMap α β` is a map from keys of type `α` to values of type `β`,
which also maintains the insertion order of keys.
Internally `IndexMap` is implementented redundantly as a `HashMap` from keys to indices
(and hence the key type must be `Hashable`), along with `Array`s of keys and values.
These implementation details are private, and hidden from the user.
-/
structure IndexMap (α : Type u) (β : Type v) [BEq α] [Hashable α] where
private indices : HashMap α Nat
private keys : Array α
private values : Array β
private size_keys' : keys.size = values.size := by grind
private WF : ∀ (i : Nat) (a : α), keys[i]? = some a ↔ indices[a]? = some i := by grind
namespace IndexMap
variable {α : Type u} {β : Type v} [BEq α] [Hashable α]
variable {m : IndexMap α β} {a : α} {b : β} {i : Nat}
@[inline] def size (m : IndexMap α β) : Nat :=
m.values.size
@[local grind =]
private theorem size_keys : m.keys.size = m.size := size_keys' _
@[local grind =]
private theorem size_values : m.values.size = m.size := rfl
def emptyWithCapacity (capacity := 8) : IndexMap α β where
indices := HashMap.emptyWithCapacity capacity
keys := Array.emptyWithCapacity capacity
values := Array.emptyWithCapacity capacity
instance : EmptyCollection (IndexMap α β) where
emptyCollection := emptyWithCapacity
instance : Inhabited (IndexMap α β) where
default := ∅
@[inline] def contains (m : IndexMap α β) (a : α) : Bool :=
m.indices.contains a
instance : Membership α (IndexMap α β) where
mem m a := a ∈ m.indices
instance {m : IndexMap α β} {a : α} : Decidable (a ∈ m) :=
inferInstanceAs (Decidable (a ∈ m.indices))
@[local grind _=_]
private theorem mem_indices {m : IndexMap α β} {a : α} :
a ∈ m.indices ↔ a ∈ m := by rfl
@[inline] def findIdx? (m : IndexMap α β) (a : α) : Option Nat := m.indices[a]?
@[inline] def findIdx (m : IndexMap α β) (a : α) (h : a ∈ m := by get_elem_tactic) : Nat := m.indices[a]
@[inline] def getIdx? (m : IndexMap α β) (i : Nat) : Option β := m.values[i]?
@[inline] def getIdx (m : IndexMap α β) (i : Nat) (h : i < m.size := by get_elem_tactic) : β :=
m.values[i]
variable [LawfulBEq α] [LawfulHashable α]
attribute [local grind _=_] IndexMap.WF
instance : GetElem? (IndexMap α β) α β (fun m a => a ∈ m) where
getElem m a h := m.values[m.indices[a]'h]
getElem? m a := m.indices[a]?.bind (fun i => (m.values[i]?))
getElem! m a := m.indices[a]?.bind (fun i => (m.values[i]?)) |>.getD default
@[local grind =]
private theorem getElem_def (m : IndexMap α β) (a : α) (h : a ∈ m) :
m[a] = m.values[m.indices[a]'h] := rfl
@[local grind =]
private theorem getElem?_def (m : IndexMap α β) (a : α) :
m[a]? = m.indices[a]?.bind (fun i => (m.values[i]?)) := rfl
@[local grind =]
private theorem getElem!_def [Inhabited β] (m : IndexMap α β) (a : α) :
m[a]! = (m.indices[a]?.bind (fun i => (m.values[i]?))).getD default := rfl
instance : LawfulGetElem (IndexMap α β) α β (fun m a => a ∈ m) where
getElem?_def := by grind
getElem!_def := by grind
@[inline] def insert (m : IndexMap α β) (a : α) (b : β) : IndexMap α β :=
match h : m.indices[a]? with
| some i =>
{ indices := m.indices
keys := m.keys.set i a
values := m.values.set i b }
| none =>
{ indices := m.indices.insert a m.size
keys := m.keys.push a
values := m.values.push b }
instance : Singleton (α × β) (IndexMap α β) :=
⟨fun ⟨a, b⟩ => (∅ : IndexMap α β).insert a b⟩
instance : Insert (α × β) (IndexMap α β) :=
⟨fun ⟨a, b⟩ s => s.insert a b⟩
instance : LawfulSingleton (α × β) (IndexMap α β) :=
⟨fun _ => rfl⟩
/--
Erase the key-value pair with the given key, moving the last pair into its place in the order.
If the key is not present, the map is unchanged.
-/
@[inline] def eraseSwap (m : IndexMap α β) (a : α) : IndexMap α β :=
match h : m.indices[a]? with
| some i =>
if w : i = m.size - 1 then
{ indices := m.indices.erase a
keys := m.keys.pop
values := m.values.pop }
else
let lastKey := m.keys.back
let lastValue := m.values.back
{ indices := (m.indices.erase a).insert lastKey i
keys := m.keys.pop.set i lastKey
values := m.values.pop.set i lastValue }
| none => m
-- TODO: similarly define `eraseShift`, etc.
/-! ### Verification theorems (not exhaustive) -/
@[grind =]
theorem mem_insert (m : IndexMap α β) (a a' : α) (b : β) :
a' ∈ m.insert a b ↔ a' = a a' ∈ m := by
grind +locals
@[grind =]
theorem getElem_insert (m : IndexMap α β) (a a' : α) (b : β) (h : a' ∈ m.insert a b) :
(m.insert a b)[a'] = if h' : a' == a then b else m[a'] := by
grind +locals
theorem findIdx_lt (m : IndexMap α β) (a : α) (h : a ∈ m) :
m.findIdx a h < m.size := by
grind +locals
grind_pattern findIdx_lt => m.findIdx a h
@[grind =]
theorem findIdx_insert_self (m : IndexMap α β) (a : α) (b : β) :
(m.insert a b).findIdx a = if h : a ∈ m then m.findIdx a else m.size := by
grind +locals
@[grind =]
theorem findIdx?_eq (m : IndexMap α β) (a : α) :
m.findIdx? a = if h : a ∈ m then some (m.findIdx a h) else none := by
grind +locals
@[grind =]
theorem getIdx_findIdx (m : IndexMap α β) (a : α) (h : a ∈ m) :
m.getIdx (m.findIdx a) = m[a] := by grind +locals
omit [LawfulBEq α] [LawfulHashable α] in
@[grind =]
theorem getIdx?_eq (m : IndexMap α β) (i : Nat) :
m.getIdx? i = if h : i < m.size then some (m.getIdx i h) else none := by
grind +locals
private theorem getElem_keys_mem {m : IndexMap α β} {i : Nat} (h : i < m.size) :
m.keys[i] ∈ m := by
have : m.indices[m.keys[i]]? = some i := by grind
grind
local grind_pattern getElem_keys_mem => m.keys[i]
theorem getElem?_eraseSwap (m : IndexMap α β) (a a' : α) :
(m.eraseSwap a)[a']? = if a' == a then none else m[a']? := by
grind +locals
@[grind =]
theorem mem_eraseSwap (m : IndexMap α β) (a a' : α) :
a' ∈ m.eraseSwap a ↔ a' ≠ a ∧ a' ∈ m := by
grind +locals
theorem getElem_eraseSwap (m : IndexMap α β) (a a' : α) (h : a' ∈ m.eraseSwap a) :
(m.eraseSwap a)[a'] = m[a'] := by
grind +locals
end IndexMap