Compare commits

...

26 Commits

Author SHA1 Message Date
Kim Morrison
5dc043aeef cleanup 2025-06-04 08:23:49 +10:00
Kim Morrison
c097603c5b clean up 2025-06-03 22:51:01 +10:00
Kim Morrison
66e4b95ac2 slow 2025-06-03 19:04:38 +10:00
Kim Morrison
d007f53203 Merge remote-tracking branch 'origin/master' into grind_indexmap 2025-06-03 18:29:02 +10:00
Kim Morrison
98ec6bc3a3 . 2025-06-02 19:48:57 +10:00
Kim Morrison
c35cb2b3d2 . 2025-06-02 17:56:26 +10:00
Kim Morrison
571d42758e Merge remote-tracking branch 'origin/master' into grind_indexmap 2025-06-02 17:48:38 +10:00
Kim Morrison
715b86089d . 2025-06-02 17:48:32 +10:00
Kim Morrison
24ef2e8043 not quite there 2025-05-30 17:01:06 +10:00
Kim Morrison
def74c39a9 indexmap looking lovely 2025-05-30 15:18:08 +10:00
Kim Morrison
901a33a097 Merge remote-tracking branch 'origin/master' into grind_indexmap 2025-05-30 10:23:09 +10:00
Kim Morrison
d5962991af redesign 2025-05-27 18:42:46 +10:00
Kim Morrison
e998c61cf2 comment 2025-05-26 16:06:12 +10:00
Kim Morrison
35d3df910d . 2025-05-26 08:59:43 +10:00
Kim Morrison
6ec3389402 update comment; one error gone 2025-05-25 16:00:07 +10:00
Kim Morrison
a802560981 Merge remote-tracking branch 'origin/master' into grind_indexmap 2025-05-25 15:38:45 +10:00
Kim Morrison
2b8d0dc456 . 2025-05-24 22:44:49 +10:00
Kim Morrison
7b00092683 Merge remote-tracking branch 'origin/master' into grind_indexmap 2025-05-24 22:21:35 +10:00
Kim Morrison
b57b7461d0 Merge branch 'getElem_lemmas' into grind_indexmap 2025-05-24 22:21:25 +10:00
Kim Morrison
9a2fe073ba fix test 2025-05-24 21:34:02 +10:00
Kim Morrison
bcec192e18 feat: generic GetElem lemmas 2025-05-24 21:05:47 +10:00
Kim Morrison
bba02e3128 . 2025-05-24 18:20:14 +10:00
Kim Morrison
d0d45a15a1 Merge branch 'grind_size_eq_zero_iff' into grind_indexmap 2025-05-24 14:08:09 +10:00
Kim Morrison
30fa545e1f chore: remove grind from Array.size_eq_zero_iff 2025-05-24 14:00:28 +10:00
Kim Morrison
e32761a8f4 . 2025-05-24 13:59:27 +10:00
Kim Morrison
d33cf59c4c initial draft of grind_indexmap example 2025-05-24 13:57:57 +10:00
3 changed files with 295 additions and 1 deletions

View File

@@ -176,7 +176,7 @@ theorem getElem_push_lt {xs : Array α} {x : α} {i : Nat} (h : i < xs.size) :
simp only [push, getElem_toList, List.concat_eq_append]
rw [List.getElem_append_right] <;> simp [ getElem_toList, Nat.zero_lt_one]
theorem getElem_push {xs : Array α} {x : α} {i : Nat} (h : i < (xs.push x).size) :
@[grind =] theorem getElem_push {xs : Array α} {x : α} {i : Nat} (h : i < (xs.push x).size) :
(xs.push x)[i] = if h : i < xs.size then xs[i] else x := by
by_cases h' : i < xs.size
· simp [getElem_push_lt, h']
@@ -954,6 +954,13 @@ theorem set_push {xs : Array α} {x y : α} {h} :
· simp at h
omega
@[grind _=_]
theorem set_pop {xs : Array α} {x : α} {i : Nat} (h : i < xs.pop.size) :
xs.pop.set i x h = (xs.set i x (by simp at h; omega)).pop := by
ext i h₁ h₂
· simp
· simp [getElem_set]
@[simp] theorem set_eq_empty_iff {xs : Array α} {i : Nat} {a : α} {h : i < xs.size} :
xs.set i a = #[] xs = #[] := by
cases xs <;> cases i <;> simp [set]

View File

@@ -0,0 +1,162 @@
-- 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
set_option grind.warning false
-- These are not good `grind` lemmas at present; they really slow things down.
-- TODO: remove globally, or work out how to make them usable.
attribute [-grind] getElem?_pos getElem?_neg getElem!_pos getElem!_neg
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| grind)
open Std
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 := m.size_keys'
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_of_mem {m : IndexMap α β} {a : α} :
a m a m.indices := Iff.rfl
variable [LawfulBEq α] [LawfulHashable α]
attribute [local grind _=_] IndexMap.WF
private theorem getElem_indices_lt {h : a m} : m.indices[a] < m.size := by
have : m.indices[a]? = some m.indices[a] := by grind [getElem?_pos]
grind
grind_pattern getElem_indices_lt => m.indices[a]
attribute [local grind] size
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
@[local grind] private theorem WF' (i : Nat) (a : α) (h₁ : i < m.keys.size) (h₂ : a m) :
m.keys[i] = a m.indices[a] = i := by
have := m.WF i a
grind [getElem?_pos]
@[local grind] private theorem getElem_keys_getElem_indices
{m : IndexMap α β} {a : α} {h : a m} :
m.keys[m.indices[a]'h] = a := by grind
@[inline] def findIdx? (m : IndexMap α β) (a : α) : Option Nat := m.indices[a]?
@[inline] def findIdx (m : IndexMap α β) (a : α) (h : a m) : 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]
instance : LawfulGetElem (IndexMap α β) α β (fun m a => a m) where
getElem?_def := by grind [getElem?_pos]
getElem!_def := by grind [getElem!_pos]
@[inline] def insert [LawfulBEq α] (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 [LawfulBEq α] : Singleton (α × β) (IndexMap α β) :=
fun a, b => ( : IndexMap α β).insert a b
instance [LawfulBEq α] : Insert (α × β) (IndexMap α β) :=
fun a, b s => s.insert a b
instance [LawfulBEq α] : 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
/-! ### Verification theorems -/
attribute [local grind] getIdx findIdx insert
@[grind] theorem getIdx_findIdx (m : IndexMap α β) (a : α) (h : a m) :
m.getIdx (m.findIdx a h) = m[a] := by grind
@[grind] theorem mem_insert (m : IndexMap α β) (a a' : α) (b : β) :
a' m.insert a b a' = a a' m := by
grind
@[grind] theorem getElem_insert (m : IndexMap α β) (a a' : α) (b : β) (h : a' m.insert a b) :
(m.insert a b)[a']'h = if h' : a' == a then b else m[a'] := by
grind
@[grind] theorem findIdx_insert_self (m : IndexMap α β) (a : α) (b : β) :
(m.insert a b).findIdx a (by grind) = if h : a m then m.findIdx a h else m.size := by
grind
end IndexMap

View File

@@ -0,0 +1,125 @@
-- This is a companion file for `grind_indexmap.lean`,
-- showing what an outline of this file might look like before any proofs are written.
import Std.Data.HashMap
set_option grind.warning false
open Std
structure IndexMap (α : Type u) (β : Type v) [BEq α] [Hashable α] where
indices : HashMap α Nat
keys : Array α
values : Array β
size_keys' : keys.size = values.size := by grind
WF : (i : Nat) (a : α), keys[i]? = some a indices[a]? = some i := by grind
namespace IndexMap
variable {α : Type u} {β : Type v} [BEq α] [LawfulBEq α] [Hashable α] [LawfulHashable α]
variable {m : IndexMap α β} {a : α} {b : β} {i : Nat}
@[inline] def size (m : IndexMap α β) : Nat :=
m.values.size
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))
instance : GetElem? (IndexMap α β) α β (fun m a => a m) where
getElem m a h := m.values[m.indices[a]'h]'(by sorry)
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
@[inline] def findIdx? (m : IndexMap α β) (a : α) : Option Nat := m.indices[a]?
@[inline] def findIdx (m : IndexMap α β) (a : α) (h : a m) : 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]
instance : LawfulGetElem (IndexMap α β) α β (fun m a => a m) where
getElem?_def := sorry
getElem!_def := sorry
@[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 sorry
values := m.values.set i b sorry
size_keys' := sorry
WF := sorry }
| none =>
{ indices := m.indices.insert a m.size
keys := m.keys.push a
values := m.values.push b
size_keys' := sorry
WF := sorry }
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
size_keys' := sorry
WF := sorry }
else
let lastKey := m.keys.back sorry
let lastValue := m.values.back sorry
{ indices := (m.indices.erase a).insert lastKey i
keys := m.keys.pop.set i lastKey sorry
values := m.values.pop.set i lastValue sorry
size_keys' := sorry
WF := sorry }
| none => m
/-! ### Verification theorems -/
theorem getIdx_findIdx (m : IndexMap α β) (a : α) (h : a m) :
m.getIdx (m.findIdx a h) sorry = m[a] := sorry
theorem mem_insert (m : IndexMap α β) (a a' : α) (b : β) :
a' m.insert a b a' = a a' m := by
sorry
theorem getElem_insert (m : IndexMap α β) (a a' : α) (b : β) (h : a' m.insert a b) :
(m.insert a b)[a']'h = if h' : a' == a then b else m[a']'sorry := by
sorry
theorem findIdx_insert_self (m : IndexMap α β) (a : α) (b : β) :
(m.insert a b).findIdx a sorry = if h : a m then m.findIdx a h else m.size := by
sorry
end IndexMap