Skip to content

Commit

Permalink
internal/slice tests+comments
Browse files Browse the repository at this point in the history
  • Loading branch information
sgreben committed Oct 8, 2024
1 parent fa817cb commit 1f380b4
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 3 deletions.
10 changes: 7 additions & 3 deletions internal/slice/count_unique.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package slice

// CountUniqueInSorted counts the number of unique elements in a sorted slice.
// It assumes the input slice is already sorted.
func CountUniqueInSorted[T comparable](s []T) int {
out := 0
var previous T
Expand Down Expand Up @@ -50,9 +52,11 @@ func GroupSorted[E any, K comparable](s []E, sKeys []K) (map[K]IndexRange, []K)
previousIdx = i
}
}
groups[previous] = IndexRange{
Offset: previousIdx,
Length: len(s) - previousIdx,
if len(s) > 0 {
groups[previous] = IndexRange{
Offset: previousIdx,
Length: len(s) - previousIdx,
}
}
}
return groups, keys
Expand Down
3 changes: 3 additions & 0 deletions internal/slice/or_alloc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package slice

// OrAlloc ensures that a slice has the specified length.
// If the input slice is shorter, it's extended if possible, and reallocated otherwise.
// If it's longer, it's truncated.
func OrAlloc[T any](s []T, n int) []T {
if len(s) == n {
return s
Expand Down
2 changes: 2 additions & 0 deletions internal/slice/reorder.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package slice

// ReorderInPlace reorders elements in a slice based on the provided indices.
// The swap function is used to perform the actual swapping of elements.
func ReorderInPlace(swap func(i, j int), indices []int) {
for i, targetIdx := range indices {
for targetIdx < i {
Expand Down
131 changes: 131 additions & 0 deletions internal/slice/slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package slice_test

import (
"reflect"
"testing"

"github.com/keilerkonzept/bitknn/internal/slice"
)

func TestCountUniqueInSorted(t *testing.T) {
tests := []struct {
name string
input []int
expected int
}{
{"Empty slice", []int{}, 0},
{"Single element", []int{1}, 1},
{"All unique", []int{1, 2, 3, 4, 5}, 5},
{"Some duplicates", []int{1, 1, 2, 3, 3, 4, 5, 5}, 5},
{"All duplicates", []int{1, 1, 1, 1, 1}, 1},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := slice.CountUniqueInSorted(tt.input)
if result != tt.expected {
t.Errorf("CountUniqueInSorted(%v) = %d, want %d", tt.input, result, tt.expected)
}
})
}
}

func TestGroupSorted(t *testing.T) {
tests := []struct {
name string
input []int
keys []string
expectedGroups map[string]slice.IndexRange
expectedKeys []string
}{
{
name: "Empty slices",
input: []int{},
keys: []string{},
expectedGroups: map[string]slice.IndexRange{},
expectedKeys: []string{},
},
{
name: "Single group",
input: []int{1, 2, 3},
keys: []string{"a", "a", "a"},
expectedGroups: map[string]slice.IndexRange{"a": {Offset: 0, Length: 3}},
expectedKeys: []string{"a"},
},
{
name: "Multiple groups",
input: []int{1, 2, 3, 4, 5, 6},
keys: []string{"a", "a", "b", "b", "c", "c"},
expectedGroups: map[string]slice.IndexRange{
"a": {Offset: 0, Length: 2},
"b": {Offset: 2, Length: 2},
"c": {Offset: 4, Length: 2},
},
expectedKeys: []string{"a", "b", "c"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
groups, keys := slice.GroupSorted(tt.input, tt.keys)
if !reflect.DeepEqual(groups, tt.expectedGroups) {
t.Errorf("GroupSorted() groups = %v, want %v", groups, tt.expectedGroups)
}
if !reflect.DeepEqual(keys, tt.expectedKeys) {
t.Errorf("GroupSorted() keys = %v, want %v", keys, tt.expectedKeys)
}
})
}
}

func TestOrAlloc(t *testing.T) {
tests := []struct {
name string
input []int
n int
expected []int
}{
{"Empty slice, n=0", []int{}, 0, []int{}},
{"Empty slice, n>0", []int{}, 3, []int{0, 0, 0}},
{"Slice shorter than n, reuse", []int{1, 2, 3, 4}[:2], 4, []int{1, 2, 3, 4}},
{"Slice shorter than n, realloc", []int{1, 2}, 4, []int{0, 0, 0, 0}},
{"Slice longer than n", []int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3}},
{"Slice equal to n", []int{1, 2, 3}, 3, []int{1, 2, 3}},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := slice.OrAlloc(tt.input, tt.n)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("OrAlloc(%v, %d) = %v, want %v", tt.input, tt.n, result, tt.expected)
}
})
}
}

func TestReorderInPlace(t *testing.T) {
tests := []struct {
name string
input []int
indices []int
expected []int
}{
{"Empty slice", []int{}, []int{}, []int{}},
{"No reordering", []int{1, 2, 3}, []int{0, 1, 2}, []int{1, 2, 3}},
{"Simple reordering", []int{1, 2, 3}, []int{2, 0, 1}, []int{3, 1, 2}},
{"Complex reordering", []int{1, 2, 3, 4, 5}, []int{4, 3, 2, 1, 0}, []int{5, 4, 3, 2, 1}},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := make([]int, len(tt.input))
copy(input, tt.input)
slice.ReorderInPlace(func(i, j int) {
input[i], input[j] = input[j], input[i]
}, tt.indices)
if !reflect.DeepEqual(input, tt.expected) {
t.Errorf("ReorderInPlace() result = %v, want %v", input, tt.expected)
}
})
}
}

0 comments on commit 1f380b4

Please sign in to comment.