173 lines
6.3 KiB
Text
173 lines
6.3 KiB
Text
/-
|
||
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.list.basic
|
||
import init.data.option.basic init.data.hashable
|
||
universes u v w
|
||
|
||
def bucketArray (α : Type u) (β : α → Type v) :=
|
||
{ b : array (list (Σ a, β a)) // b.sz > 0 }
|
||
|
||
def bucketArray.uwrite {α : Type u} {β : α → Type v} (data : bucketArray α β) (i : usize) (d : list (Σ a, β a)) (h : i.toNat < data.val.sz) : bucketArray α β :=
|
||
⟨ data.val.uwrite i d h,
|
||
transRelRight gt (array.szWriteEq (data.val) ⟨usize.toNat i, h⟩ d) data.property ⟩
|
||
|
||
structure hashmapImp (α : Type u) (β : α → Type v) :=
|
||
(size : nat)
|
||
(buckets : bucketArray α β)
|
||
|
||
def mkHashmapImp {α : Type u} {β : α → Type v} (nbuckets := 8) : hashmapImp α β :=
|
||
let n := if nbuckets = 0 then 8 else nbuckets in
|
||
{ size := 0,
|
||
buckets :=
|
||
⟨ mkArray n [],
|
||
have p₁ : (mkArray n ([] : list (Σ a, β a))).sz = 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 gt (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 : bucketArray α β) (a : α) (b : β a) : bucketArray α β :=
|
||
let ⟨i, h⟩ := mkIdx data.property (hashFn a) in
|
||
data.uwrite i (⟨a, b⟩ :: data.val.uread i h) h
|
||
|
||
def foldBuckets {δ : Type w} (data : bucketArray α β) (d : δ) (f : δ → Π a, β a → δ) : δ :=
|
||
data.val.foldl d (λ b d, b.foldl (λ r (p : Σ a, β a), f r p.1 p.2) d)
|
||
|
||
def findAux [decidableEq α] (a : α) : list (Σ a, β a) → option (β a)
|
||
| [] := none
|
||
| (⟨a',b⟩::t) :=
|
||
if h : a' = a then some (eq.recOn h b) else findAux t
|
||
|
||
def containsAux [decidableEq α] (a : α) (l : list (Σ a, β a)) : bool :=
|
||
(findAux a l).isSome
|
||
|
||
def find [decidableEq α] [hashable α] (m : hashmapImp α β) (a : α) : option (β a) :=
|
||
match m with
|
||
| ⟨_, buckets⟩ :=
|
||
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
|
||
findAux a (buckets.val.uread i h)
|
||
|
||
def fold {δ : Type w} (m : hashmapImp α β) (d : δ) (f : δ → Π a, β a → δ) : δ :=
|
||
foldBuckets m.buckets d f
|
||
|
||
def replaceAux [decidableEq α] (a : α) (b : β a) : list (Σ a, β a) → list (Σ a, β a)
|
||
| [] := []
|
||
| (⟨a', b'⟩::t) := if a' = a then ⟨a, b⟩::t else ⟨a', b'⟩ :: replaceAux t
|
||
|
||
def eraseAux [decidableEq α] (a : α) : list (Σ a, β a) → list (Σ a, β a)
|
||
| [] := []
|
||
| (⟨a', b'⟩::t) := if a' = a then t else ⟨a', b'⟩ :: eraseAux t
|
||
|
||
def insert [decidableEq α] [hashable α] (m : hashmapImp α β) (a : α) (b : β a) : hashmapImp α β :=
|
||
match m with
|
||
| ⟨size, buckets⟩ :=
|
||
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
|
||
let bkt := buckets.val.uread i h in
|
||
if containsAux a bkt
|
||
then ⟨size, buckets.uwrite i (replaceAux a b bkt) h⟩
|
||
else let size' := size + 1 in
|
||
let buckets' := buckets.uwrite i (⟨a, b⟩::bkt) h in
|
||
if size' <= buckets.val.sz
|
||
then ⟨size', buckets'⟩
|
||
else let nbuckets' := buckets.val.sz * 2 in
|
||
let nz' : nbuckets' > 0 := nat.mulPos buckets.property (nat.zeroLtBit0 nat.oneNeZero) in
|
||
⟨ size',
|
||
foldBuckets buckets' ⟨mkArray nbuckets' [], nz'⟩ (reinsertAux hash) ⟩
|
||
|
||
def erase [decidableEq α] [hashable α] (m : hashmapImp α β) (a : α) : hashmapImp α β :=
|
||
match m with
|
||
| ⟨ size, buckets ⟩ :=
|
||
let ⟨i, h⟩ := mkIdx buckets.property (hash a) in
|
||
let bkt := buckets.val.uread i h in
|
||
if containsAux a bkt then ⟨size - 1, buckets.uwrite i (eraseAux a bkt) h⟩
|
||
else m
|
||
|
||
inductive wellFormed [decidableEq α] [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 dHashmap (α : Type u) (β : α → Type v) [decidableEq α] [hashable α] :=
|
||
{ m : hashmapImp α β // m.wellFormed }
|
||
|
||
open hashmapImp
|
||
|
||
def mkDHashmap {α : Type u} {β : α → Type v} [decidableEq α] [hashable α] (nbuckets := 8) : dHashmap α β :=
|
||
⟨ mkHashmapImp nbuckets, wellFormed.mkWff nbuckets ⟩
|
||
|
||
namespace dHashmap
|
||
variables {α : Type u} {β : α → Type v} [decidableEq α] [hashable α]
|
||
|
||
def insert (m : dHashmap α β) (a : α) (b : β a) : dHashmap α β :=
|
||
match m with
|
||
| ⟨ m, hw ⟩ := ⟨ m.insert a b, wellFormed.insertWff m a b hw ⟩
|
||
|
||
def erase (m : dHashmap α β) (a : α) : dHashmap α β :=
|
||
match m with
|
||
| ⟨ m, hw ⟩ := ⟨ m.erase a, wellFormed.eraseWff m a hw ⟩
|
||
|
||
def find (m : dHashmap α β) (a : α) : option (β a) :=
|
||
match m with
|
||
| ⟨ m, _ ⟩ := m.find a
|
||
|
||
@[inline] def contains (m : dHashmap α β) (a : α) : bool :=
|
||
(m.find a).isSome
|
||
|
||
def fold {δ : Type w} (m : dHashmap α β) (d : δ) (f : δ → Π a, β a → δ) : δ :=
|
||
match m with
|
||
| ⟨ m, _ ⟩ := m.fold d f
|
||
|
||
def size (m : dHashmap α β) : nat :=
|
||
match m with
|
||
| ⟨ {size := sz, ..}, _ ⟩ := sz
|
||
|
||
@[inline] def empty (m : dHashmap α β) : bool :=
|
||
m.size = 0
|
||
|
||
end dHashmap
|
||
|
||
def hashmap (α : Type u) (β : Type v) [decidableEq α] [hashable α] :=
|
||
dHashmap α (λ _, β)
|
||
|
||
def mkHashmap {α : Type u} {β : Type v} [decidableEq α] [hashable α] (nbuckets := 8) : hashmap α β :=
|
||
mkDHashmap nbuckets
|
||
|
||
namespace hashmap
|
||
variables {α : Type u} {β : Type v} [decidableEq α] [hashable α]
|
||
|
||
@[inline] def insert (m : hashmap α β) (a : α) (b : β) : hashmap α β :=
|
||
dHashmap.insert m a b
|
||
|
||
@[inline] def erase (m : hashmap α β) (a : α) : hashmap α β :=
|
||
dHashmap.erase m a
|
||
|
||
@[inline] def find (m : hashmap α β) (a : α) : option β :=
|
||
dHashmap.find m a
|
||
|
||
@[inline] def contains (m : hashmap α β) (a : α) : bool :=
|
||
(m.find a).isSome
|
||
|
||
@[inline] def fold {δ : Type w} (m : hashmap α β) (d : δ) (f : δ → α → β → δ) : δ :=
|
||
dHashmap.fold m d f
|
||
|
||
@[inline] def size (m : hashmap α β) : nat :=
|
||
dHashmap.size m
|
||
|
||
@[inline] def empty (m : hashmap α β) : bool :=
|
||
dHashmap.empty m
|
||
|
||
end hashmap
|