mirror of
https://github.com/luxfi/math.git
synced 2026-07-27 03:38:49 +00:00
- Add Zero[T any]() generic function for zero values - Add IsSortedBytes for byte slice sorting checks - Add heap/queue.go with priority Queue[T] implementation - Add heap/map.go with indexed Map[K,V] for heap operations - Add linked/list.go with doubly-linked List[T] - Add linked/hashmap.go with ordered HashMap[K,V] - Update sampleable_set.go to use local luxmath.Zero[T]() - Update averager_heap.go to use github.com/luxfi/math/heap - Update safe_math.go to use luxmath.Zero[T]() - Remove github.com/luxfi/utils dependency entirely
167 lines
3.9 KiB
Go
167 lines
3.9 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package linked
|
|
|
|
import luxmath "github.com/luxfi/math"
|
|
|
|
type keyValue[K, V any] struct {
|
|
key K
|
|
value V
|
|
}
|
|
|
|
// Hashmap provides an ordered O(1) mapping from keys to values.
|
|
//
|
|
// Entries are tracked by insertion order.
|
|
type Hashmap[K comparable, V any] struct {
|
|
entryMap map[K]*ListElement[keyValue[K, V]]
|
|
entryList *List[keyValue[K, V]]
|
|
freeList []*ListElement[keyValue[K, V]]
|
|
}
|
|
|
|
func NewHashmap[K comparable, V any]() *Hashmap[K, V] {
|
|
return NewHashmapWithSize[K, V](0)
|
|
}
|
|
|
|
func NewHashmapWithSize[K comparable, V any](initialSize int) *Hashmap[K, V] {
|
|
lh := &Hashmap[K, V]{
|
|
entryMap: make(map[K]*ListElement[keyValue[K, V]], initialSize),
|
|
entryList: NewList[keyValue[K, V]](),
|
|
freeList: make([]*ListElement[keyValue[K, V]], initialSize),
|
|
}
|
|
for i := range lh.freeList {
|
|
lh.freeList[i] = &ListElement[keyValue[K, V]]{}
|
|
}
|
|
return lh
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Put(key K, value V) {
|
|
if e, ok := lh.entryMap[key]; ok {
|
|
lh.entryList.MoveToBack(e)
|
|
e.Value = keyValue[K, V]{
|
|
key: key,
|
|
value: value,
|
|
}
|
|
return
|
|
}
|
|
|
|
var e *ListElement[keyValue[K, V]]
|
|
if numFree := len(lh.freeList); numFree > 0 {
|
|
numFree--
|
|
e = lh.freeList[numFree]
|
|
lh.freeList = lh.freeList[:numFree]
|
|
} else {
|
|
e = &ListElement[keyValue[K, V]]{}
|
|
}
|
|
|
|
e.Value = keyValue[K, V]{
|
|
key: key,
|
|
value: value,
|
|
}
|
|
lh.entryMap[key] = e
|
|
lh.entryList.PushBack(e)
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Get(key K) (V, bool) {
|
|
if e, ok := lh.entryMap[key]; ok {
|
|
return e.Value.value, true
|
|
}
|
|
return luxmath.Zero[V](), false
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Delete(key K) bool {
|
|
e, ok := lh.entryMap[key]
|
|
if ok {
|
|
lh.remove(e)
|
|
}
|
|
return ok
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Clear() {
|
|
for _, e := range lh.entryMap {
|
|
lh.remove(e)
|
|
}
|
|
}
|
|
|
|
// remove assumes that [e] is currently in the Hashmap.
|
|
func (lh *Hashmap[K, V]) remove(e *ListElement[keyValue[K, V]]) {
|
|
delete(lh.entryMap, e.Value.key)
|
|
lh.entryList.Remove(e)
|
|
e.Value = keyValue[K, V]{} // Free the key value pair
|
|
lh.freeList = append(lh.freeList, e)
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Len() int {
|
|
return len(lh.entryMap)
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Oldest() (K, V, bool) {
|
|
if e := lh.entryList.Front(); e != nil {
|
|
return e.Value.key, e.Value.value, true
|
|
}
|
|
return luxmath.Zero[K](), luxmath.Zero[V](), false
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) Newest() (K, V, bool) {
|
|
if e := lh.entryList.Back(); e != nil {
|
|
return e.Value.key, e.Value.value, true
|
|
}
|
|
return luxmath.Zero[K](), luxmath.Zero[V](), false
|
|
}
|
|
|
|
func (lh *Hashmap[K, V]) NewIterator() *Iterator[K, V] {
|
|
return &Iterator[K, V]{lh: lh}
|
|
}
|
|
|
|
// Iterates over the keys and values in a LinkedHashmap from oldest to newest.
|
|
// Assumes the underlying LinkedHashmap is not modified while the iterator is in
|
|
// use, except to delete elements that have already been iterated over.
|
|
type Iterator[K comparable, V any] struct {
|
|
lh *Hashmap[K, V]
|
|
key K
|
|
value V
|
|
next *ListElement[keyValue[K, V]]
|
|
initialized, exhausted bool
|
|
}
|
|
|
|
func (it *Iterator[K, V]) Next() bool {
|
|
// If the iterator has been exhausted, there is no next value.
|
|
if it.exhausted {
|
|
it.key = luxmath.Zero[K]()
|
|
it.value = luxmath.Zero[V]()
|
|
it.next = nil
|
|
return false
|
|
}
|
|
|
|
// If the iterator was not yet initialized, do it now.
|
|
if !it.initialized {
|
|
it.initialized = true
|
|
oldest := it.lh.entryList.Front()
|
|
if oldest == nil {
|
|
it.exhausted = true
|
|
it.key = luxmath.Zero[K]()
|
|
it.value = luxmath.Zero[V]()
|
|
it.next = nil
|
|
return false
|
|
}
|
|
it.next = oldest
|
|
}
|
|
|
|
// It's important to ensure that [it.next] is not nil
|
|
// by not deleting elements that have not yet been iterated
|
|
// over from [it.lh]
|
|
it.key = it.next.Value.key
|
|
it.value = it.next.Value.value
|
|
it.next = it.next.Next() // Next time, return next element
|
|
it.exhausted = it.next == nil
|
|
return true
|
|
}
|
|
|
|
func (it *Iterator[K, V]) Key() K {
|
|
return it.key
|
|
}
|
|
|
|
func (it *Iterator[K, V]) Value() V {
|
|
return it.value
|
|
}
|