Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/zyedidia/generic
module github.com/st1064870/generic

go 1.18

Expand Down
131 changes: 89 additions & 42 deletions hashmap/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@ package hashmap

import (
g "github.com/zyedidia/generic"
"math/rand"
)

type entry[K, V any] struct {
key K
filled bool
value V
}

// A Map is a hashmap that supports copying via copy-on-write.
type Map[K, V any] struct {
entries []entry[K, V]
keys []K
values []V
filled []bool
capacity uint64
length uint64
readonly bool
Expand Down Expand Up @@ -46,7 +43,9 @@ func New[K, V any](capacity uint64, equals g.EqualsFn[K], hash g.HashFn[K]) *Map
}
capacity = pow2ceil(capacity)
return &Map[K, V]{
entries: make([]entry[K, V], capacity),
keys: make([]K, capacity),
values: make([]V, capacity),
filled: make([]bool, capacity),
capacity: capacity,
ops: ops[K]{
equals: equals,
Expand All @@ -61,9 +60,9 @@ func (m *Map[K, V]) Get(key K) (V, bool) {
hash := m.ops.hash(key)
idx := hash & (m.capacity - 1)

for m.entries[idx].filled {
if m.ops.equals(m.entries[idx].key, key) {
return m.entries[idx].value, true
for m.filled[idx] {
if m.ops.equals(m.keys[idx], key) {
return m.values[idx], true
}
idx++
if idx >= m.capacity {
Expand All @@ -79,17 +78,22 @@ func (m *Map[K, V]) resize(newcap uint64) {
newm := Map[K, V]{
capacity: newcap,
length: m.length,
entries: make([]entry[K, V], newcap),
keys: make([]K, newcap),
values: make([]V, newcap),
filled: make([]bool, newcap),
ops: m.ops,
}

for _, ent := range m.entries {
if ent.filled {
newm.Put(ent.key, ent.value)
for i := range m.keys {
if m.filled[i] {
newm.Put(m.keys[i], m.values[i])
}
}

m.capacity = newm.capacity
m.entries = newm.entries
m.keys = newm.keys
m.values = newm.values
m.filled = newm.filled
}

// Put maps the given key to the given value. If the key already exists its
Expand All @@ -98,18 +102,24 @@ func (m *Map[K, V]) Put(key K, val V) {
if m.length >= m.capacity/2 {
m.resize(m.capacity * 2)
} else if m.readonly {
entries := make([]entry[K, V], len(m.entries), cap(m.entries))
copy(entries, m.entries)
m.entries = entries
keys := make([]K, len(m.keys), cap(m.keys))
values := make([]V, len(m.values), cap(m.values))
filled := make([]bool, len(m.filled), cap(m.filled))
copy(keys, m.keys)
copy(values, m.values)
copy(filled, m.filled)
m.keys = keys
m.values = values
m.filled = filled
m.readonly = false
}

hash := m.ops.hash(key)
idx := hash & (m.capacity - 1)

for m.entries[idx].filled {
if m.ops.equals(m.entries[idx].key, key) {
m.entries[idx].value = val
for m.filled[idx] {
if m.ops.equals(m.keys[idx], key) {
m.values[idx] = val
return
}
idx++
Expand All @@ -118,18 +128,18 @@ func (m *Map[K, V]) Put(key K, val V) {
}
}

m.entries[idx].key = key
m.entries[idx].value = val
m.entries[idx].filled = true
m.keys[idx] = key
m.values[idx] = val
m.filled[idx] = true
m.length++
}

func (m *Map[K, V]) remove(idx uint64) {
var k K
var v V
m.entries[idx].filled = false
m.entries[idx].key = k
m.entries[idx].value = v
m.filled[idx] = false
m.keys[idx] = k
m.values[idx] = v
m.length--
}

Expand All @@ -138,27 +148,33 @@ func (m *Map[K, V]) Remove(key K) {
hash := m.ops.hash(key)
idx := hash & (m.capacity - 1)

for m.entries[idx].filled && !m.ops.equals(m.entries[idx].key, key) {
for m.filled[idx] && !m.ops.equals(m.keys[idx], key) {
idx = (idx + 1) & (m.capacity - 1)
}

if !m.entries[idx].filled {
if !m.filled[idx] {
return
}

if m.readonly {
entries := make([]entry[K, V], len(m.entries), cap(m.entries))
copy(entries, m.entries)
m.entries = entries
keys := make([]K, len(m.keys), cap(m.keys))
values := make([]V, len(m.values), cap(m.values))
filled := make([]bool, len(m.filled), cap(m.filled))
copy(keys, m.keys)
copy(values, m.values)
copy(filled, m.filled)
m.keys = keys
m.values = values
m.filled = filled
m.readonly = false
}

m.remove(idx)

idx = (idx + 1) & (m.capacity - 1)
for m.entries[idx].filled {
krehash := m.entries[idx].key
vrehash := m.entries[idx].value
for m.filled[idx] {
krehash := m.keys[idx]
vrehash := m.values[idx]
m.remove(idx)
m.Put(krehash, vrehash)
idx = (idx + 1) & (m.capacity - 1)
Expand All @@ -172,8 +188,8 @@ func (m *Map[K, V]) Remove(key K) {

// Clear removes all key-value pairs from the map.
func (m *Map[K, V]) Clear() {
for idx, entry := range m.entries {
if entry.filled {
for idx := range m.keys {
if m.filled[idx] {
m.remove(uint64(idx))
}
}
Expand All @@ -190,7 +206,9 @@ func (m *Map[K, V]) Size() int {
func (m *Map[K, V]) Copy() *Map[K, V] {
m.readonly = true
return &Map[K, V]{
entries: m.entries,
keys: m.keys,
values: m.values,
filled: m.filled,
capacity: m.capacity,
length: m.length,
readonly: true,
Expand All @@ -201,9 +219,38 @@ func (m *Map[K, V]) Copy() *Map[K, V] {
// Each calls 'fn' on every key-value pair in the hashmap in no particular
// order.
func (m *Map[K, V]) Each(fn func(key K, val V)) {
for _, ent := range m.entries {
if ent.filled {
fn(ent.key, ent.value)
for idx := range m.keys {
if m.filled[idx] {
fn(m.keys[idx], m.values[idx])
}
}
}

// Keys returns the key of the hashmap
func (m *Map[K, V]) Keys() []K {
keys := make([]K, len(m.keys))

for idx := range m.keys {
if m.filled[idx] {
keys = append(keys, m.keys[idx])
}
}
return keys[:m.Size()]
}

// Values returns the values of the hashmap
func (m *Map[K, V]) Values() []V {
values := make([]V, len(m.keys))

for idx := range m.keys {
if m.filled[idx] {
values = append(values, m.values[idx])
}
}
return values[:m.Size()]
}

func (m *Map[K, V]) Random() (K, V) {
randomIndex := rand.Intn(m.Size())
return m.Keys()[randomIndex], m.Values()[randomIndex]
}
9 changes: 9 additions & 0 deletions hashset/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package hashset
import (
g "github.com/zyedidia/generic"
"github.com/zyedidia/generic/hashmap"
"math/rand"
)

// Set implements a hashset, using the hashmap as the underlying storage.
Expand Down Expand Up @@ -66,3 +67,11 @@ func (s *Set[K]) Copy() *Set[K] {
m: s.m.Copy(),
}
}

func (s *Set[K]) Values() []K {
return s.m.Keys()
}

func (s *Set[K]) Random() K {
return s.Values()[rand.Intn(s.Size())]
}
16 changes: 8 additions & 8 deletions set/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ func (s Set[K]) ConstUnion(with ...K) Set[K] {
}

func (s Set[K]) Clone() Set[K] {
new := NewSet(s.new)
s.Each(func(key K) { new.Put(key) })
return new
news := NewSet(s.new)
s.Each(func(key K) { news.Put(key) })
return news
}

func (s Set[K]) String() string {
Expand All @@ -91,19 +91,19 @@ func (s Set[K]) Map() map[K]struct{} {
}

func (s Set[K]) SymmetricDifference(others ...SetOf[K]) Set[K] {
new := s.Clone()
seen := new.Clone()
news := s.Clone()
seen := news.Clone()
for _, other := range others {
other.Each(func(key K) {
if seen.Has(key) {
new.Remove(key)
news.Remove(key)
return
}
new.Put(key)
news.Put(key)
seen.Put(key)
})
}
return new
return news
}

func (s Set[K]) InPlaceIntersection(others ...SetOf[K]) Set[K] {
Expand Down