lean4-htt/library/init/data/hashmap/basic.lean
Leonardo de Moura d9be0290ae perf(library/init/data/hashmap/basic): missing @[inline]
This commit adds the auxiliary function `expand` to break a nasty
interaction between code specialization, erasure and memory reuse.

Suppose we have
```lean
let  new_array := array.update array i v in
have pr : <some property that mentions array>, from <proof>,
f  new_array pr
```
Suppose `f` is not marked with `[specialize]`. Then, `pr` is erased and we get:

```lean
let  new_array := array.update array i v in
f  new_array box(0)
```
If `RC(array) == 1`, then the update performs a destructive update, and
we are happy.

Now, assume that `f` is marked with `[specialize]`.
Moreover, function specialization occurs before erasure since we want to
be able to use the to-be-implemented user-defined rewriting rules after specialization.
When we specialize `f` we compute a closure of its dependencies, and one of them is array because of `pr`.
Thus, we get
```lean
let  new_array := array.update array i v in
f_spec new_array array
```
after specialization and erasure, but now, we don't perfom the destructive update.

BTW, I assumed we should be able to reduce the arity of `f_spec` after
erasure since the parameter `array` be dead after it. However, I found the following TODO:
https://github.com/leanprover/lean4/blob/master/src/library/compiler/reduce_arity.cpp#L50
2019-04-03 09:38:03 -07:00

143 lines
5.3 KiB
Text
Raw 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.

/-
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import init.data.array.basic init.data.assoclist
import init.data.option.basic init.data.hashable
universes u v w
def HashMapBucket (α : Type u) (β : Type v) :=
{ b : Array (AssocList α β) // b.size > 0 }
def HashMapBucket.update {α : Type u} {β : Type v} (data : HashMapBucket α β) (i : USize) (d : AssocList α β) (h : i.toNat < data.val.size) : HashMapBucket α β :=
⟨ data.val.updt i d h,
transRelRight Greater (Array.szUpdateEq (data.val) ⟨USize.toNat i, h⟩ d) data.property ⟩
structure HashMapImp (α : Type u) (β : Type v) :=
(size : Nat)
(buckets : HashMapBucket α β)
def mkHashMapImp {α : Type u} {β : Type v} (nbuckets := 8) : HashMapImp α β :=
let n := if nbuckets = 0 then 8 else nbuckets in
{ size := 0,
buckets :=
⟨ mkArray n AssocList.nil,
have p₁ : (mkArray n (@AssocList.nil α β)).size = n, from szMkArrayEq _ _,
have p₂ : n = (if nbuckets = 0 then 8 else nbuckets), from rfl,
have p₃ : (if nbuckets = 0 then 8 else nbuckets) > 0, from
match nbuckets with
| 0 := Nat.zeroLtSucc _
| (Nat.succ x) := Nat.zeroLtSucc _,
transRelRight Greater (Eq.trans p₁ p₂) p₃ ⟩ }
namespace HashMapImp
variables {α : Type u} {β : Type v}
def mkIdx {n : Nat} (h : n > 0) (u : USize) : { u : USize // u.toNat < n } :=
⟨u %ₙ n, USize.modnLt _ h⟩
def reinsertAux (hashFn : α → USize) (data : HashMapBucket α β) (a : α) (b : β) : HashMapBucket α β :=
let ⟨i, h⟩ := mkIdx data.property (hashFn a) in
data.update i (AssocList.cons a b (data.val.idx i h)) h
@[inline] def foldBuckets {δ : Type w} (data : HashMapBucket α β) (d : δ) (f : δ → α → β → δ) : δ :=
data.val.foldl (λ b d, b.foldl f d) d
def find [HasBeq α] [Hashable α] (m : HashMapImp α β) (a : α) : Option β :=
match m with
| ⟨_, buckets⟩ :=
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
(buckets.val.idx i h).find a
def contains [HasBeq α] [Hashable α] (m : HashMapImp α β) (a : α) : Bool :=
match m with
| ⟨_, buckets⟩ :=
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
(buckets.val.idx i h).contains a
@[inline] def fold {δ : Type w} (f : δ → α → β → δ) (d : δ) (m : HashMapImp α β) : δ :=
foldBuckets m.buckets d f
def expand [Hashable α] (size : Nat) (buckets : HashMapBucket α β) : HashMapImp α β :=
let nbuckets := buckets.val.size * 2 in
let aux₁ : nbuckets > 0 := Nat.mulPos buckets.property (Nat.zeroLtBit0 Nat.oneNeZero) in
let aux₂ : (mkArray nbuckets (@AssocList.nil α β)).size = nbuckets := szMkArrayEq _ _ in
{ size := size,
buckets := foldBuckets buckets ⟨mkArray nbuckets AssocList.nil, aux₂.symm ▸ aux₁⟩ (reinsertAux hash) }
def insert [HasBeq α] [Hashable α] (m : HashMapImp α β) (a : α) (b : β) : HashMapImp α β :=
match m with
| ⟨size, buckets⟩ :=
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
let bkt := buckets.val.idx i h in
if bkt.contains a
then ⟨size, buckets.update i (bkt.replace a b) h⟩
else
let size' := size + 1 in
let buckets' := buckets.update i (AssocList.cons a b bkt) h in
if size' ≤ buckets.val.size
then { size := size', buckets := buckets' }
else expand size' buckets'
def erase [HasBeq α] [Hashable α] (m : HashMapImp α β) (a : α) : HashMapImp α β :=
match m with
| ⟨ size, buckets ⟩ :=
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
let bkt := buckets.val.idx i h in
if bkt.contains a then ⟨size - 1, buckets.update i (bkt.erase a) h⟩
else m
inductive WellFormed [HasBeq α] [Hashable α] : HashMapImp α β → Prop
| mkWff : ∀ n, WellFormed (mkHashMapImp n)
| insertWff : ∀ m a b, WellFormed m → WellFormed (insert m a b)
| eraseWff : ∀ m a, WellFormed m → WellFormed (erase m a)
end HashMapImp
def HashMap (α : Type u) (β : Type v) [HasBeq α] [Hashable α] :=
{ m : HashMapImp α β // m.WellFormed }
open HashMapImp
def mkHashMap {α : Type u} {β : Type v} [HasBeq α] [Hashable α] (nbuckets := 8) : HashMap α β :=
⟨ mkHashMapImp nbuckets, WellFormed.mkWff nbuckets ⟩
namespace HashMap
variables {α : Type u} {β : Type v} [HasBeq α] [Hashable α]
instance : Inhabited (HashMap α β) :=
⟨mkHashMap⟩
instance : HasEmptyc (HashMap α β) :=
⟨mkHashMap⟩
@[inline] def insert (m : HashMap α β) (a : α) (b : β) : HashMap α β :=
match m with
| ⟨ m, hw ⟩ := ⟨ m.insert a b, WellFormed.insertWff m a b hw ⟩
@[inline] def erase (m : HashMap α β) (a : α) : HashMap α β :=
match m with
| ⟨ m, hw ⟩ := ⟨ m.erase a, WellFormed.eraseWff m a hw ⟩
@[inline] def find (m : HashMap α β) (a : α) : Option β :=
match m with
| ⟨ m, _ ⟩ := m.find a
@[inline] def contains (m : HashMap α β) (a : α) : Bool :=
match m with
| ⟨ m, _ ⟩ := m.contains a
@[inline] def fold {δ : Type w} (f : δ → α → β → δ) (d : δ) (m : HashMap α β) : δ :=
match m with
| ⟨ m, _ ⟩ := m.fold f d
@[inline] def size (m : HashMap α β) : Nat :=
match m with
| ⟨ {size := sz, ..}, _ ⟩ := sz
@[inline] def empty (m : HashMap α β) : Bool :=
m.size = 0
end HashMap