mirror of
https://github.com/luxfi/math.git
synced 2026-07-27 03:38:49 +00:00
feat: add heap, linked, sorting, zero - remove utils dependency
- 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
This commit is contained in:
@@ -6,14 +6,12 @@ require (
|
|||||||
github.com/luxfi/geth v1.16.52
|
github.com/luxfi/geth v1.16.52
|
||||||
github.com/luxfi/ids v1.2.4
|
github.com/luxfi/ids v1.2.4
|
||||||
github.com/luxfi/sampler v1.0.0
|
github.com/luxfi/sampler v1.0.0
|
||||||
github.com/luxfi/utils v1.0.0
|
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
|
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/gorilla/rpc v1.2.1 // indirect
|
|
||||||
github.com/luxfi/crypto v1.17.25 // indirect
|
github.com/luxfi/crypto v1.17.25 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
|
|
||||||
github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptRfGk=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@@ -14,8 +12,6 @@ github.com/luxfi/ids v1.2.4 h1:e0OaeSI6xWjS9JnxxxfvCCs9HHDUrwDc3M9ctIhjcDI=
|
|||||||
github.com/luxfi/ids v1.2.4/go.mod h1:HwvRJSNbuZS+u+0JqeHfPX2S13iFyLCnfGxjBCmt244=
|
github.com/luxfi/ids v1.2.4/go.mod h1:HwvRJSNbuZS+u+0JqeHfPX2S13iFyLCnfGxjBCmt244=
|
||||||
github.com/luxfi/sampler v1.0.0 h1:k8Sf6otW83w4pQp0jXLA+g3J/joB7w7SqXQsWmNTOV0=
|
github.com/luxfi/sampler v1.0.0 h1:k8Sf6otW83w4pQp0jXLA+g3J/joB7w7SqXQsWmNTOV0=
|
||||||
github.com/luxfi/sampler v1.0.0/go.mod h1:f96/ozlj9vFfZj+akLtrHn4VpulQahwB+MQQhpeIekk=
|
github.com/luxfi/sampler v1.0.0/go.mod h1:f96/ozlj9vFfZj+akLtrHn4VpulQahwB+MQQhpeIekk=
|
||||||
github.com/luxfi/utils v1.0.0 h1:eibxyw/xkwnB9KRSmYvKLbdLTUElyfJpxLIBeTBKqlU=
|
|
||||||
github.com/luxfi/utils v1.0.0/go.mod h1:ABqhBdGNig0CaDcnNYldv1byS0BTNi5IKPbJSPF1p98=
|
|
||||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
|||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package heap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
|
||||||
|
luxmath "github.com/luxfi/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ heap.Interface = (*indexedQueue[int, int])(nil)
|
||||||
|
|
||||||
|
func MapValues[K comparable, V any](m Map[K, V]) []V {
|
||||||
|
result := make([]V, 0, m.Len())
|
||||||
|
for _, e := range m.queue.entries {
|
||||||
|
result = append(result, e.v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMap returns a heap without duplicates ordered by its values
|
||||||
|
func NewMap[K comparable, V any](less func(a, b V) bool) Map[K, V] {
|
||||||
|
return Map[K, V]{
|
||||||
|
queue: &indexedQueue[K, V]{
|
||||||
|
queue: queue[entry[K, V]]{
|
||||||
|
less: func(a, b entry[K, V]) bool {
|
||||||
|
return less(a.v, b.v)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
index: make(map[K]int),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Map[K comparable, V any] struct {
|
||||||
|
queue *indexedQueue[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push returns the evicted previous value if present
|
||||||
|
func (m *Map[K, V]) Push(k K, v V) (V, bool) {
|
||||||
|
if i, ok := m.queue.index[k]; ok {
|
||||||
|
prev := m.queue.entries[i]
|
||||||
|
m.queue.entries[i].v = v
|
||||||
|
heap.Fix(m.queue, i)
|
||||||
|
return prev.v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
heap.Push(m.queue, entry[K, V]{k: k, v: v})
|
||||||
|
return luxmath.Zero[V](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Pop() (K, V, bool) {
|
||||||
|
if m.Len() == 0 {
|
||||||
|
return luxmath.Zero[K](), luxmath.Zero[V](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
popped := heap.Pop(m.queue).(entry[K, V])
|
||||||
|
return popped.k, popped.v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Peek() (K, V, bool) {
|
||||||
|
if m.Len() == 0 {
|
||||||
|
return luxmath.Zero[K](), luxmath.Zero[V](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := m.queue.entries[0]
|
||||||
|
return entry.k, entry.v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Len() int {
|
||||||
|
return m.queue.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Remove(k K) (V, bool) {
|
||||||
|
if i, ok := m.queue.index[k]; ok {
|
||||||
|
removed := heap.Remove(m.queue, i).(entry[K, V])
|
||||||
|
return removed.v, true
|
||||||
|
}
|
||||||
|
return luxmath.Zero[V](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Contains(k K) bool {
|
||||||
|
_, ok := m.queue.index[k]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Get(k K) (V, bool) {
|
||||||
|
if i, ok := m.queue.index[k]; ok {
|
||||||
|
got := m.queue.entries[i]
|
||||||
|
return got.v, true
|
||||||
|
}
|
||||||
|
return luxmath.Zero[V](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map[K, V]) Fix(k K) {
|
||||||
|
if i, ok := m.queue.index[k]; ok {
|
||||||
|
heap.Fix(m.queue, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexedQueue[K comparable, V any] struct {
|
||||||
|
queue[entry[K, V]]
|
||||||
|
index map[K]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *indexedQueue[K, V]) Swap(i, j int) {
|
||||||
|
h.entries[i], h.entries[j] = h.entries[j], h.entries[i]
|
||||||
|
h.index[h.entries[i].k], h.index[h.entries[j].k] = i, j
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *indexedQueue[K, V]) Push(x any) {
|
||||||
|
entry := x.(entry[K, V])
|
||||||
|
h.entries = append(h.entries, entry)
|
||||||
|
h.index[entry.k] = len(h.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *indexedQueue[K, V]) Pop() any {
|
||||||
|
end := len(h.entries) - 1
|
||||||
|
|
||||||
|
popped := h.entries[end]
|
||||||
|
h.entries[end] = entry[K, V]{}
|
||||||
|
h.entries = h.entries[:end]
|
||||||
|
|
||||||
|
delete(h.index, popped.k)
|
||||||
|
return popped
|
||||||
|
}
|
||||||
|
|
||||||
|
type entry[K any, V any] struct {
|
||||||
|
k K
|
||||||
|
v V
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package heap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
|
||||||
|
luxmath "github.com/luxfi/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ heap.Interface = (*queue[int])(nil)
|
||||||
|
|
||||||
|
// NewQueue returns an empty heap. See QueueOf for more.
|
||||||
|
func NewQueue[T any](less func(a, b T) bool) Queue[T] {
|
||||||
|
return QueueOf(less)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueOf returns a heap containing entries ordered by less.
|
||||||
|
func QueueOf[T any](less func(a, b T) bool, entries ...T) Queue[T] {
|
||||||
|
q := Queue[T]{
|
||||||
|
queue: &queue[T]{
|
||||||
|
entries: make([]T, len(entries)),
|
||||||
|
less: less,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(q.queue.entries, entries)
|
||||||
|
heap.Init(q.queue)
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queue[T any] struct {
|
||||||
|
queue *queue[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue[T]) Len() int {
|
||||||
|
return len(q.queue.entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue[T]) Push(t T) {
|
||||||
|
heap.Push(q.queue, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue[T]) Pop() (T, bool) {
|
||||||
|
if q.Len() == 0 {
|
||||||
|
return luxmath.Zero[T](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
return heap.Pop(q.queue).(T), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue[T]) Peek() (T, bool) {
|
||||||
|
if q.Len() == 0 {
|
||||||
|
return luxmath.Zero[T](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.queue.entries[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue[T]) Fix(i int) {
|
||||||
|
heap.Fix(q.queue, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
type queue[T any] struct {
|
||||||
|
entries []T
|
||||||
|
less func(a, b T) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queue[T]) Len() int {
|
||||||
|
return len(q.entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queue[T]) Less(i, j int) bool {
|
||||||
|
return q.less(q.entries[i], q.entries[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queue[T]) Swap(i, j int) {
|
||||||
|
q.entries[i], q.entries[j] = q.entries[j], q.entries[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queue[T]) Push(e any) {
|
||||||
|
q.entries = append(q.entries, e.(T))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queue[T]) Pop() any {
|
||||||
|
end := len(q.entries) - 1
|
||||||
|
|
||||||
|
popped := q.entries[end]
|
||||||
|
q.entries[end] = luxmath.Zero[T]()
|
||||||
|
q.entries = q.entries[:end]
|
||||||
|
|
||||||
|
return popped
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
+217
@@ -0,0 +1,217 @@
|
|||||||
|
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package linked
|
||||||
|
|
||||||
|
// ListElement is an element of a linked list.
|
||||||
|
type ListElement[T any] struct {
|
||||||
|
next, prev *ListElement[T]
|
||||||
|
list *List[T]
|
||||||
|
Value T
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next element or nil.
|
||||||
|
func (e *ListElement[T]) Next() *ListElement[T] {
|
||||||
|
if p := e.next; e.list != nil && p != &e.list.sentinel {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev returns the previous element or nil.
|
||||||
|
func (e *ListElement[T]) Prev() *ListElement[T] {
|
||||||
|
if p := e.prev; e.list != nil && p != &e.list.sentinel {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List implements a doubly linked list with a sentinel node.
|
||||||
|
//
|
||||||
|
// See: https://en.wikipedia.org/wiki/Doubly_linked_list
|
||||||
|
//
|
||||||
|
// This datastructure is designed to be an almost complete drop-in replacement
|
||||||
|
// for the standard library's "container/list".
|
||||||
|
//
|
||||||
|
// The primary design change is to remove all memory allocations from the list
|
||||||
|
// definition. This allows these lists to be used in performance critical paths.
|
||||||
|
// Additionally the zero value is not useful. Lists must be created with the
|
||||||
|
// NewList method.
|
||||||
|
type List[T any] struct {
|
||||||
|
// sentinel is only used as a placeholder to avoid complex nil checks.
|
||||||
|
// sentinel.Value is never used.
|
||||||
|
sentinel ListElement[T]
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewList creates a new doubly linked list.
|
||||||
|
func NewList[T any]() *List[T] {
|
||||||
|
l := &List[T]{}
|
||||||
|
l.sentinel.next = &l.sentinel
|
||||||
|
l.sentinel.prev = &l.sentinel
|
||||||
|
l.sentinel.list = l
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of elements in l.
|
||||||
|
func (l *List[_]) Len() int {
|
||||||
|
return l.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Front returns the element at the front of l.
|
||||||
|
// If l is empty, nil is returned.
|
||||||
|
func (l *List[T]) Front() *ListElement[T] {
|
||||||
|
if l.length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.sentinel.next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back returns the element at the back of l.
|
||||||
|
// If l is empty, nil is returned.
|
||||||
|
func (l *List[T]) Back() *ListElement[T] {
|
||||||
|
if l.length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.sentinel.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes e from l if e is in l.
|
||||||
|
func (l *List[T]) Remove(e *ListElement[T]) {
|
||||||
|
if e.list != l {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.prev.next = e.next
|
||||||
|
e.next.prev = e.prev
|
||||||
|
e.next = nil
|
||||||
|
e.prev = nil
|
||||||
|
e.list = nil
|
||||||
|
l.length--
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront inserts e at the front of l.
|
||||||
|
// If e is already in a list, l is not modified.
|
||||||
|
func (l *List[T]) PushFront(e *ListElement[T]) {
|
||||||
|
l.insertAfter(e, &l.sentinel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack inserts e at the back of l.
|
||||||
|
// If e is already in a list, l is not modified.
|
||||||
|
func (l *List[T]) PushBack(e *ListElement[T]) {
|
||||||
|
l.insertAfter(e, l.sentinel.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertBefore inserts e immediately before location.
|
||||||
|
// If e is already in a list, l is not modified.
|
||||||
|
// If location is not in l, l is not modified.
|
||||||
|
func (l *List[T]) InsertBefore(e *ListElement[T], location *ListElement[T]) {
|
||||||
|
if location.list == l {
|
||||||
|
l.insertAfter(e, location.prev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAfter inserts e immediately after location.
|
||||||
|
// If e is already in a list, l is not modified.
|
||||||
|
// If location is not in l, l is not modified.
|
||||||
|
func (l *List[T]) InsertAfter(e *ListElement[T], location *ListElement[T]) {
|
||||||
|
if location.list == l {
|
||||||
|
l.insertAfter(e, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveToFront moves e to the front of l.
|
||||||
|
// If e is not in l, l is not modified.
|
||||||
|
func (l *List[T]) MoveToFront(e *ListElement[T]) {
|
||||||
|
// If e is already at the front of l, there is nothing to do.
|
||||||
|
if e != l.sentinel.next {
|
||||||
|
l.moveAfter(e, &l.sentinel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveToBack moves e to the back of l.
|
||||||
|
// If e is not in l, l is not modified.
|
||||||
|
func (l *List[T]) MoveToBack(e *ListElement[T]) {
|
||||||
|
l.moveAfter(e, l.sentinel.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveBefore moves e immediately before location.
|
||||||
|
// If the elements are equal or not in l, the list is not modified.
|
||||||
|
func (l *List[T]) MoveBefore(e, location *ListElement[T]) {
|
||||||
|
// Don't introduce a cycle by moving an element before itself.
|
||||||
|
if e != location {
|
||||||
|
l.moveAfter(e, location.prev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveAfter moves e immediately after location.
|
||||||
|
// If the elements are equal or not in l, the list is not modified.
|
||||||
|
func (l *List[T]) MoveAfter(e, location *ListElement[T]) {
|
||||||
|
l.moveAfter(e, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List[T]) insertAfter(e, location *ListElement[T]) {
|
||||||
|
if e.list != nil {
|
||||||
|
// Don't insert an element that is already in a list
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.prev = location
|
||||||
|
e.next = location.next
|
||||||
|
e.prev.next = e
|
||||||
|
e.next.prev = e
|
||||||
|
e.list = l
|
||||||
|
l.length++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List[T]) moveAfter(e, location *ListElement[T]) {
|
||||||
|
if e.list != l || location.list != l || e == location {
|
||||||
|
// Don't modify an element that is in a different list.
|
||||||
|
// Don't introduce a cycle by moving an element after itself.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.prev.next = e.next
|
||||||
|
e.next.prev = e.prev
|
||||||
|
|
||||||
|
e.prev = location
|
||||||
|
e.next = location.next
|
||||||
|
e.prev.next = e
|
||||||
|
e.next.prev = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront inserts v into a new element at the front of l.
|
||||||
|
func PushFront[T any](l *List[T], v T) {
|
||||||
|
l.PushFront(&ListElement[T]{
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack inserts v into a new element at the back of l.
|
||||||
|
func PushBack[T any](l *List[T], v T) {
|
||||||
|
l.PushBack(&ListElement[T]{
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertBefore inserts v into a new element immediately before location.
|
||||||
|
// If location is not in l, l is not modified.
|
||||||
|
func InsertBefore[T any](l *List[T], v T, location *ListElement[T]) {
|
||||||
|
l.InsertBefore(
|
||||||
|
&ListElement[T]{
|
||||||
|
Value: v,
|
||||||
|
},
|
||||||
|
location,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertAfter inserts v into a new element immediately after location.
|
||||||
|
// If location is not in l, l is not modified.
|
||||||
|
func InsertAfter[T any](l *List[T], v T, location *ListElement[T]) {
|
||||||
|
l.InsertAfter(
|
||||||
|
&ListElement[T]{
|
||||||
|
Value: v,
|
||||||
|
},
|
||||||
|
location,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ package math
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/luxfi/ids"
|
"github.com/luxfi/ids"
|
||||||
"github.com/luxfi/utils/heap"
|
"github.com/luxfi/math/heap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ AveragerHeap = (*averagerHeap)(nil)
|
var _ AveragerHeap = (*averagerHeap)(nil)
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
|
|
||||||
"github.com/luxfi/utils"
|
luxmath "github.com/luxfi/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -36,7 +36,7 @@ func Add64(a, b uint64) (uint64, error) {
|
|||||||
// 2) If there is underflow, an error
|
// 2) If there is underflow, an error
|
||||||
func Sub[T constraints.Unsigned](a, b T) (T, error) {
|
func Sub[T constraints.Unsigned](a, b T) (T, error) {
|
||||||
if a < b {
|
if a < b {
|
||||||
return utils.Zero[T](), ErrUnderflow
|
return luxmath.Zero[T](), ErrUnderflow
|
||||||
}
|
}
|
||||||
return a - b, nil
|
return a - b, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-21
@@ -8,11 +8,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
luxmath "github.com/luxfi/math"
|
||||||
"github.com/luxfi/sampler"
|
"github.com/luxfi/sampler"
|
||||||
"github.com/luxfi/utils"
|
|
||||||
"github.com/luxfi/utils/wrappers"
|
|
||||||
|
|
||||||
luxjson "github.com/luxfi/utils/json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const minSetSize = 16
|
const minSetSize = 16
|
||||||
@@ -111,7 +108,7 @@ func (s *SampleableSet[T]) Remove(elements ...T) {
|
|||||||
func (s *SampleableSet[T]) Clear() {
|
func (s *SampleableSet[T]) Clear() {
|
||||||
clear(s.indices)
|
clear(s.indices)
|
||||||
for i := range s.elements {
|
for i := range s.elements {
|
||||||
s.elements[i] = utils.Zero[T]()
|
s.elements[i] = luxmath.Zero[T]()
|
||||||
}
|
}
|
||||||
s.elements = s.elements[:0]
|
s.elements = s.elements[:0]
|
||||||
}
|
}
|
||||||
@@ -151,7 +148,7 @@ func (s SampleableSet[T]) Sample(numToSample int) []T {
|
|||||||
|
|
||||||
func (s *SampleableSet[T]) UnmarshalJSON(b []byte) error {
|
func (s *SampleableSet[T]) UnmarshalJSON(b []byte) error {
|
||||||
str := string(b)
|
str := string(b)
|
||||||
if str == luxjson.Null {
|
if str == "null" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var elements []T
|
var elements []T
|
||||||
@@ -178,24 +175,16 @@ func (s *SampleableSet[_]) MarshalJSON() ([]byte, error) {
|
|||||||
slices.SortFunc(elementBytes, bytes.Compare)
|
slices.SortFunc(elementBytes, bytes.Compare)
|
||||||
|
|
||||||
// Build the JSON
|
// Build the JSON
|
||||||
var (
|
var jsonBuf bytes.Buffer
|
||||||
jsonBuf = bytes.Buffer{}
|
jsonBuf.WriteString("[")
|
||||||
errs = wrappers.Errs{}
|
|
||||||
)
|
|
||||||
_, err = jsonBuf.WriteString("[")
|
|
||||||
errs.Add(err)
|
|
||||||
for i, elt := range elementBytes {
|
for i, elt := range elementBytes {
|
||||||
_, err := jsonBuf.Write(elt)
|
jsonBuf.Write(elt)
|
||||||
errs.Add(err)
|
|
||||||
if i != len(elementBytes)-1 {
|
if i != len(elementBytes)-1 {
|
||||||
_, err := jsonBuf.WriteString(",")
|
jsonBuf.WriteString(",")
|
||||||
errs.Add(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = jsonBuf.WriteString("]")
|
jsonBuf.WriteString("]")
|
||||||
errs.Add(err)
|
return jsonBuf.Bytes(), nil
|
||||||
|
|
||||||
return jsonBuf.Bytes(), errs.Err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SampleableSet[T]) resize(size int) {
|
func (s *SampleableSet[T]) resize(size int) {
|
||||||
@@ -232,6 +221,6 @@ func (s *SampleableSet[T]) remove(e T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete(s.indices, e)
|
delete(s.indices, e)
|
||||||
s.elements[lastIndex] = utils.Zero[T]()
|
s.elements[lastIndex] = luxmath.Zero[T]()
|
||||||
s.elements = s.elements[:lastIndex]
|
s.elements = s.elements[:lastIndex]
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package math
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
// IsSortedBytes returns true if s is sorted in ascending order.
|
||||||
|
func IsSortedBytes[T ~[]byte](s []T) bool {
|
||||||
|
for i := 0; i < len(s)-1; i++ {
|
||||||
|
if bytes.Compare(s[i], s[i+1]) == 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user