From 97014203e4bdb29ec1bb55f4fd357aabdacf37a9 Mon Sep 17 00:00:00 2001 From: emilis Date: Tue, 2 Aug 2022 13:18:08 +0100 Subject: [PATCH] added helper pkgs --- flabk/ap/object_test.go | 2 +- pkg/coll/Iterator.go | 19 +++++ pkg/coll/coll.go | 72 ++++++++++++++++++ pkg/coll/vector.go | 165 ++++++++++++++++++++++++++++++++++++++++ pkg/coll/vector_test.go | 152 ++++++++++++++++++++++++++++++++++++ 5 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 pkg/coll/Iterator.go create mode 100644 pkg/coll/coll.go create mode 100644 pkg/coll/vector.go create mode 100644 pkg/coll/vector_test.go diff --git a/flabk/ap/object_test.go b/flabk/ap/object_test.go index a7158ac..8d639ef 100644 --- a/flabk/ap/object_test.go +++ b/flabk/ap/object_test.go @@ -6,7 +6,7 @@ import ( "github.com/go-ap/jsonld" "github.com/stretchr/testify/require" - "sectorinf.com/emilis/flabk/asf/ap" + "sectorinf.com/emilis/flabk/flabk/ap" ) func GetExample() ap.Object { diff --git a/pkg/coll/Iterator.go b/pkg/coll/Iterator.go new file mode 100644 index 0000000..e6a5c02 --- /dev/null +++ b/pkg/coll/Iterator.go @@ -0,0 +1,19 @@ +package coll + +type Iterator[T any] Vector[T] + +func (i Iterator[T]) ForEach(f func(int, T)) { + for index := 0; index < i.length; index++ { + f(index, i.array[index]) + } +} + +func (i Iterator[T]) ForBreak(f func(index int, t T, breaker func())) { + var broke bool + brk := func() { + broke = true + } + for index := 0; index < i.length || !broke; index++ { + f(index, i.array[index], brk) + } +} diff --git a/pkg/coll/coll.go b/pkg/coll/coll.go new file mode 100644 index 0000000..4cb1f5d --- /dev/null +++ b/pkg/coll/coll.go @@ -0,0 +1,72 @@ +// Package coll provides generic collection types and functions +package coll + +func Map[T, V any](c Vector[T], f func(T) V) Vector[V] { + out := make([]V, c.length) + for index := 0; index < c.length; index++ { + out[index] = f(c.array[index]) + } + + return From(out) +} + +func Filter[T any](v Vector[T], f func(T) bool) Vector[T] { + out := WithCap[T](v.length) + v.Iterate().ForEach(func(_ int, t T) { + if f(t) { + out.Push(t) + } + }) + return out +} + +func Take[T any](v Vector[T], howMany int) Vector[T] { + if v.length == 0 { + return New[T]() + } + if v.length < howMany { + howMany = v.length + } + return From(v.array[:howMany-1]) +} + +func Any[T any](v Vector[T], f func(T) bool) bool { + var found bool + v.Iterate().ForBreak(func(_ int, t T, breaker func()) { + if f(t) { + found = true + breaker() + } + }) + return found +} + +type numeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~float32 | ~float64 +} + +func Min[T numeric](v Vector[T]) T { + var min T + if v.length == 0 { + return min + } + min = v.array[0] + v.Iterate().ForEach(func(_ int, t T) { + if min > t { + min = t + } + }) + + return min +} + +func Max[T numeric](v Vector[T]) T { + var max T + v.Iterate().ForEach(func(_ int, t T) { + if max < t { + max = t + } + }) + + return max +} diff --git a/pkg/coll/vector.go b/pkg/coll/vector.go new file mode 100644 index 0000000..4dcc8ff --- /dev/null +++ b/pkg/coll/vector.go @@ -0,0 +1,165 @@ +package coll + +import "fmt" + +const ( + DefaultSize = 4 +) + +type Vector[T any] struct { + array []T + length int + capacity int +} + +func New[T any]() Vector[T] { + return WithCap[T](DefaultSize) +} + +func WithCap[T any](capacity int) Vector[T] { + return Vector[T]{ + array: make([]T, capacity), + length: 0, + capacity: capacity, + } +} + +func From[T any](v []T) Vector[T] { + return Vector[T]{ + array: v, + length: len(v), + capacity: len(v), + } +} + +func (v Vector[T]) Push(t T) Vector[T] { + if v.length == v.capacity { + v = v.upsize() + } + + v.array[v.length] = t + v.length++ + return v +} + +func (v Vector[T]) Append(t ...T) Vector[T] { + if v.capacity-v.length < len(t) { + v = v.upsizeSpecific(len(t)) + } + var tIndex int + for ; v.length < v.capacity; v.length++ { + v.array[v.length] = t[tIndex] + tIndex++ + } + return v +} + +func (v Vector[T]) Remove(index int) Vector[T] { + return From(append(v.array[:index], v.array[index+1:v.length]...)) +} + +// Faster remove function that can be used on vectors where ordering doesn't matter +func (v Vector[T]) RemoveUnordered(index int) Vector[T] { + v.length-- + v.array[index] = v.array[v.length] + return v +} + +func (v Vector[T]) upsize() Vector[T] { + return v.upsizeSpecific(v.capacity) +} + +func (v Vector[T]) upsizeSpecific(extraSize int) Vector[T] { + v.array = append(v.array, make([]T, extraSize)...) + v.capacity = len(v.array) + return v +} + +func (v Vector[T]) Slice() []T { + if v.length == 0 { + return []T{} + } + return v.array[:v.length] +} + +func (v Vector[T]) panicIndex(index int) { + min := -1 + if v.length != 0 { + min = 0 + } + panic(fmt.Sprintf("index %d out of range [%d;%d)", index, min, v.length)) +} + +// GetSoft tries to get the requested index, or returns a default value if +// it's out of range +func (v Vector[T]) GetSoft(index int) T { + var result T + if index < 0 || index >= v.length { + return result + } + return v.array[index] +} + +func (v Vector[T]) Get(index int) T { + if index < 0 || index >= v.length { + v.panicIndex(index) + } + return v.array[index] +} + +func (v Vector[T]) Set(index int, value T) Vector[T] { + if index < 0 || index >= v.length { + v.panicIndex(index) + } + v.array[index] = value + return v +} + +func (v Vector[T]) Len() int { + return v.length +} + +func (v Vector[T]) Iterate() Iterator[T] { + return Iterator[T](v) +} + +func (v Vector[T]) Pop() (T, Vector[T]) { + v.length-- + t := v.array[v.length] + return t, v +} + +func (v Vector[T]) Sub(start, end int) Vector[T] { + if start >= v.length { + v.panicIndex(start) + } + if end >= v.length { + v.panicIndex(end) + } + if start < 0 { + return From(v.array[:end]) + } + if end < 0 { + return From(v.array[start:]) + } + return From(v.array[start:end]) +} + +func (v Vector[T]) Filter(f func(T) bool) Vector[T] { + return Filter(v, f) +} + +func (v Vector[T]) Any(f func(T) bool) bool { + return Any(v, f) +} + +func (v Vector[T]) Take(howMany int) Vector[T] { + return Take(v, howMany) +} + +func (v Vector[T]) Clone() Vector[T] { + clone := make([]T, len(v.array)) + copy(clone, v.array) + v.array = clone + return v +} diff --git a/pkg/coll/vector_test.go b/pkg/coll/vector_test.go new file mode 100644 index 0000000..cbae3c0 --- /dev/null +++ b/pkg/coll/vector_test.go @@ -0,0 +1,152 @@ +package coll_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "sectorinf.com/emilis/flabk/pkg/coll" +) + +func TestVector(t *testing.T) { + tests := map[string]func(that *require.Assertions){ + "push": func(that *require.Assertions) { + v := coll.New[int]() + v = v.Push(0).Push(1).Push(2).Push(3).Push(4).Push(5) + that.Equal(6, v.Len()) + that.Len(v.Slice(), 6) + that.Equal([]int{0, 1, 2, 3, 4, 5}, v.Slice()) + }, + "len": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + that.Equal(3, v.Len()) + that.Len(v.Slice(), v.Len()) + }, + "from": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + that.Equal(3, v.Len()) + that.Len(v.Slice(), 3) + }, + "pop": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + t, v := v.Pop() + that.Equal(3, t) + that.Equal(2, v.Len()) + that.Equal([]int{1, 2}, v.Slice()) + }, + "withcap": func(that *require.Assertions) { + v := coll.WithCap[int](3) + that.Zero(v.Len()) + that.Len(v.Slice(), 0) + }, + "remove": func(that *require.Assertions) { + v := coll.New[int]().Push(0).Push(1).Push(2) + r1 := v.Remove(1) + that.Equal(2, r1.Len()) + that.Equal([]int{0, 2}, r1.Slice()) + that.Panics(func() { + v.Remove(-1) + }) + that.Panics(func() { + v.Remove(100000) + }) + }, + "remove unordered": func(that *require.Assertions) { + v := coll.New[int]().Push(0).Push(1).Push(2) + v = v.RemoveUnordered(1) + that.Equal(2, v.Len()) + vSlice := v.Slice() + if vSlice[0] == 0 { + that.Equal(2, vSlice[1]) + } else { + that.Equal(2, vSlice[0]) + that.Zero(vSlice[1]) + } + }, + "append": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}).Append(4, 5, 6) + that.Equal(6, v.Len()) + that.Equal([]int{1, 2, 3, 4, 5, 6}, v.Slice()) + }, + "getsoft": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + that.Equal(2, v.GetSoft(1)) + that.Zero(v.GetSoft(-1000)) + that.Zero(v.GetSoft(1000)) + }, + "get": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + that.Equal(2, v.Get(1)) + that.Panics(func() { + v.Get(-1000) + }) + that.Panics(func() { + v.Get(1000) + }) + }, + "clone": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + + v1 := v.Clone().Set(1, 1) + that.Equal(3, v1.Len()) + that.Equal([]int{1, 1, 3}, v1.Slice()) + that.Equal([]int{1, 2, 3}, v.Slice()) + }, + "set": func(that *require.Assertions) { + v := coll.From([]int{1, 2, 3}) + + v1 := v.Clone().Set(1, 1) + that.Equal(3, v1.Len()) + that.Equal([]int{1, 1, 3}, v1.Slice()) + + v2 := v.Clone().Set(2, 1) + that.Equal(3, v2.Len()) + that.Equal([]int{1, 2, 1}, v2.Slice()) + + v3 := v.Clone().Set(0, 16) + that.Equal(3, v3.Len()) + that.Equal([]int{16, 2, 3}, v3.Slice()) + + that.Panics(func() { + v.Set(-1000, 1) + }) + that.Panics(func() { + v.Set(3, 1) + }) + }, + "sub": func(that *require.Assertions) { + v := coll.From([]int{0, 1, 2, 3, 4, 5, 6}) + + v1 := v.Clone().Sub(2, 4) + that.Equal(2, v1.Len()) + that.Equal([]int{2, 3}, v1.Slice()) + + v2 := v.Clone().Sub(2, 5) + that.Equal(3, v2.Len()) + that.Equal([]int{2, 3, 4}, v2.Slice()) + + v3 := v.Clone().Sub(5, -1) + that.Equal(2, v3.Len()) + that.Equal([]int{5, 6}, v3.Slice()) + + v4 := v.Clone().Sub(-1, 2) + that.Equal(2, v4.Len()) + that.Equal([]int{0, 1}, v4.Slice()) + + that.Panics(func() { + v.Sub(0, 1000) + }) + that.Panics(func() { + v.Sub(4000, 4002) + }) + that.Panics(func() { + v.Sub(2, 1) + }) + }, + } + + for name, test := range tests { + t.Run(name, func(tt *testing.T) { + test(require.New(tt)) + }) + } +}