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
227 lines
5.0 KiB
Go
227 lines
5.0 KiB
Go
// Copyright (C) 2019-2024, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package set
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"slices"
|
|
|
|
luxmath "github.com/luxfi/math"
|
|
"github.com/luxfi/sampler"
|
|
)
|
|
|
|
const minSetSize = 16
|
|
|
|
var _ json.Marshaler = (*SampleableSet[int])(nil)
|
|
|
|
// SampleableSet is a set of elements that supports sampling.
|
|
type SampleableSet[T comparable] struct {
|
|
// indices maps the element in the set to the index that it appears in
|
|
// elements.
|
|
indices map[T]int
|
|
elements []T
|
|
}
|
|
|
|
// OfSampleable returns a Set initialized with [elts]
|
|
func OfSampleable[T comparable](elts ...T) SampleableSet[T] {
|
|
s := NewSampleableSet[T](len(elts))
|
|
s.Add(elts...)
|
|
return s
|
|
}
|
|
|
|
// Return a new sampleable set with initial capacity [size].
|
|
// More or less than [size] elements can be added to this set.
|
|
// Using NewSampleableSet() rather than SampleableSet[T]{} is just an
|
|
// optimization that can be used if you know how many elements will be put in
|
|
// this set.
|
|
func NewSampleableSet[T comparable](size int) SampleableSet[T] {
|
|
if size < 0 {
|
|
return SampleableSet[T]{}
|
|
}
|
|
return SampleableSet[T]{
|
|
indices: make(map[T]int, size),
|
|
elements: make([]T, 0, size),
|
|
}
|
|
}
|
|
|
|
// Add all the elements to this set.
|
|
// If the element is already in the set, nothing happens.
|
|
func (s *SampleableSet[T]) Add(elements ...T) {
|
|
s.resize(2 * len(elements))
|
|
for _, e := range elements {
|
|
s.add(e)
|
|
}
|
|
}
|
|
|
|
// Union adds all the elements from the provided set to this set.
|
|
func (s *SampleableSet[T]) Union(set SampleableSet[T]) {
|
|
s.resize(2 * set.Len())
|
|
for _, e := range set.elements {
|
|
s.add(e)
|
|
}
|
|
}
|
|
|
|
// Difference removes all the elements in [set] from [s].
|
|
func (s *SampleableSet[T]) Difference(set SampleableSet[T]) {
|
|
for _, e := range set.elements {
|
|
s.remove(e)
|
|
}
|
|
}
|
|
|
|
// Contains returns true iff the set contains this element.
|
|
func (s SampleableSet[T]) Contains(e T) bool {
|
|
_, contains := s.indices[e]
|
|
return contains
|
|
}
|
|
|
|
// Overlaps returns true if the intersection of the set is non-empty
|
|
func (s SampleableSet[T]) Overlaps(big SampleableSet[T]) bool {
|
|
small := s
|
|
if small.Len() > big.Len() {
|
|
small, big = big, small
|
|
}
|
|
|
|
for _, e := range small.elements {
|
|
if _, ok := big.indices[e]; ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Len returns the number of elements in this set.
|
|
func (s SampleableSet[_]) Len() int {
|
|
return len(s.elements)
|
|
}
|
|
|
|
// Remove all the given elements from this set.
|
|
// If an element isn't in the set, it's ignored.
|
|
func (s *SampleableSet[T]) Remove(elements ...T) {
|
|
for _, e := range elements {
|
|
s.remove(e)
|
|
}
|
|
}
|
|
|
|
// Clear empties this set
|
|
func (s *SampleableSet[T]) Clear() {
|
|
clear(s.indices)
|
|
for i := range s.elements {
|
|
s.elements[i] = luxmath.Zero[T]()
|
|
}
|
|
s.elements = s.elements[:0]
|
|
}
|
|
|
|
// List converts this set into a list
|
|
func (s SampleableSet[T]) List() []T {
|
|
return slices.Clone(s.elements)
|
|
}
|
|
|
|
// Equals returns true if the sets contain the same elements
|
|
func (s SampleableSet[T]) Equals(other SampleableSet[T]) bool {
|
|
if len(s.indices) != len(other.indices) {
|
|
return false
|
|
}
|
|
for k := range s.indices {
|
|
if _, ok := other.indices[k]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s SampleableSet[T]) Sample(numToSample int) []T {
|
|
if numToSample <= 0 {
|
|
return nil
|
|
}
|
|
|
|
uniform := sampler.NewUniform()
|
|
uniform.Initialize(uint64(len(s.elements)))
|
|
indices, _ := uniform.Sample(min(len(s.elements), numToSample))
|
|
elements := make([]T, len(indices))
|
|
for i, index := range indices {
|
|
elements[i] = s.elements[index]
|
|
}
|
|
return elements
|
|
}
|
|
|
|
func (s *SampleableSet[T]) UnmarshalJSON(b []byte) error {
|
|
str := string(b)
|
|
if str == "null" {
|
|
return nil
|
|
}
|
|
var elements []T
|
|
if err := json.Unmarshal(b, &elements); err != nil {
|
|
return err
|
|
}
|
|
s.Clear()
|
|
s.Add(elements...)
|
|
return nil
|
|
}
|
|
|
|
func (s *SampleableSet[_]) MarshalJSON() ([]byte, error) {
|
|
var (
|
|
elementBytes = make([][]byte, len(s.elements))
|
|
err error
|
|
)
|
|
for i, e := range s.elements {
|
|
elementBytes[i], err = json.Marshal(e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
// Sort for determinism
|
|
slices.SortFunc(elementBytes, bytes.Compare)
|
|
|
|
// Build the JSON
|
|
var jsonBuf bytes.Buffer
|
|
jsonBuf.WriteString("[")
|
|
for i, elt := range elementBytes {
|
|
jsonBuf.Write(elt)
|
|
if i != len(elementBytes)-1 {
|
|
jsonBuf.WriteString(",")
|
|
}
|
|
}
|
|
jsonBuf.WriteString("]")
|
|
return jsonBuf.Bytes(), nil
|
|
}
|
|
|
|
func (s *SampleableSet[T]) resize(size int) {
|
|
if s.elements == nil {
|
|
if minSetSize > size {
|
|
size = minSetSize
|
|
}
|
|
s.indices = make(map[T]int, size)
|
|
}
|
|
}
|
|
|
|
func (s *SampleableSet[T]) add(e T) {
|
|
_, ok := s.indices[e]
|
|
if ok {
|
|
return
|
|
}
|
|
|
|
s.indices[e] = len(s.elements)
|
|
s.elements = append(s.elements, e)
|
|
}
|
|
|
|
func (s *SampleableSet[T]) remove(e T) {
|
|
indexToRemove, ok := s.indices[e]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
lastIndex := len(s.elements) - 1
|
|
if indexToRemove != lastIndex {
|
|
lastElement := s.elements[lastIndex]
|
|
|
|
s.indices[lastElement] = indexToRemove
|
|
s.elements[indexToRemove] = lastElement
|
|
}
|
|
|
|
delete(s.indices, e)
|
|
s.elements[lastIndex] = luxmath.Zero[T]()
|
|
s.elements = s.elements[:lastIndex]
|
|
}
|