Skip to content

Commit 1f380b4

Browse files
committed
internal/slice tests+comments
1 parent fa817cb commit 1f380b4

4 files changed

Lines changed: 143 additions & 3 deletions

File tree

internal/slice/count_unique.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package slice
22

3+
// CountUniqueInSorted counts the number of unique elements in a sorted slice.
4+
// It assumes the input slice is already sorted.
35
func CountUniqueInSorted[T comparable](s []T) int {
46
out := 0
57
var previous T
@@ -50,9 +52,11 @@ func GroupSorted[E any, K comparable](s []E, sKeys []K) (map[K]IndexRange, []K)
5052
previousIdx = i
5153
}
5254
}
53-
groups[previous] = IndexRange{
54-
Offset: previousIdx,
55-
Length: len(s) - previousIdx,
55+
if len(s) > 0 {
56+
groups[previous] = IndexRange{
57+
Offset: previousIdx,
58+
Length: len(s) - previousIdx,
59+
}
5660
}
5761
}
5862
return groups, keys

internal/slice/or_alloc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package slice
22

3+
// OrAlloc ensures that a slice has the specified length.
4+
// If the input slice is shorter, it's extended if possible, and reallocated otherwise.
5+
// If it's longer, it's truncated.
36
func OrAlloc[T any](s []T, n int) []T {
47
if len(s) == n {
58
return s

internal/slice/reorder.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package slice
22

3+
// ReorderInPlace reorders elements in a slice based on the provided indices.
4+
// The swap function is used to perform the actual swapping of elements.
35
func ReorderInPlace(swap func(i, j int), indices []int) {
46
for i, targetIdx := range indices {
57
for targetIdx < i {

internal/slice/slice_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package slice_test
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/keilerkonzept/bitknn/internal/slice"
8+
)
9+
10+
func TestCountUniqueInSorted(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
input []int
14+
expected int
15+
}{
16+
{"Empty slice", []int{}, 0},
17+
{"Single element", []int{1}, 1},
18+
{"All unique", []int{1, 2, 3, 4, 5}, 5},
19+
{"Some duplicates", []int{1, 1, 2, 3, 3, 4, 5, 5}, 5},
20+
{"All duplicates", []int{1, 1, 1, 1, 1}, 1},
21+
}
22+
23+
for _, tt := range tests {
24+
t.Run(tt.name, func(t *testing.T) {
25+
result := slice.CountUniqueInSorted(tt.input)
26+
if result != tt.expected {
27+
t.Errorf("CountUniqueInSorted(%v) = %d, want %d", tt.input, result, tt.expected)
28+
}
29+
})
30+
}
31+
}
32+
33+
func TestGroupSorted(t *testing.T) {
34+
tests := []struct {
35+
name string
36+
input []int
37+
keys []string
38+
expectedGroups map[string]slice.IndexRange
39+
expectedKeys []string
40+
}{
41+
{
42+
name: "Empty slices",
43+
input: []int{},
44+
keys: []string{},
45+
expectedGroups: map[string]slice.IndexRange{},
46+
expectedKeys: []string{},
47+
},
48+
{
49+
name: "Single group",
50+
input: []int{1, 2, 3},
51+
keys: []string{"a", "a", "a"},
52+
expectedGroups: map[string]slice.IndexRange{"a": {Offset: 0, Length: 3}},
53+
expectedKeys: []string{"a"},
54+
},
55+
{
56+
name: "Multiple groups",
57+
input: []int{1, 2, 3, 4, 5, 6},
58+
keys: []string{"a", "a", "b", "b", "c", "c"},
59+
expectedGroups: map[string]slice.IndexRange{
60+
"a": {Offset: 0, Length: 2},
61+
"b": {Offset: 2, Length: 2},
62+
"c": {Offset: 4, Length: 2},
63+
},
64+
expectedKeys: []string{"a", "b", "c"},
65+
},
66+
}
67+
68+
for _, tt := range tests {
69+
t.Run(tt.name, func(t *testing.T) {
70+
groups, keys := slice.GroupSorted(tt.input, tt.keys)
71+
if !reflect.DeepEqual(groups, tt.expectedGroups) {
72+
t.Errorf("GroupSorted() groups = %v, want %v", groups, tt.expectedGroups)
73+
}
74+
if !reflect.DeepEqual(keys, tt.expectedKeys) {
75+
t.Errorf("GroupSorted() keys = %v, want %v", keys, tt.expectedKeys)
76+
}
77+
})
78+
}
79+
}
80+
81+
func TestOrAlloc(t *testing.T) {
82+
tests := []struct {
83+
name string
84+
input []int
85+
n int
86+
expected []int
87+
}{
88+
{"Empty slice, n=0", []int{}, 0, []int{}},
89+
{"Empty slice, n>0", []int{}, 3, []int{0, 0, 0}},
90+
{"Slice shorter than n, reuse", []int{1, 2, 3, 4}[:2], 4, []int{1, 2, 3, 4}},
91+
{"Slice shorter than n, realloc", []int{1, 2}, 4, []int{0, 0, 0, 0}},
92+
{"Slice longer than n", []int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3}},
93+
{"Slice equal to n", []int{1, 2, 3}, 3, []int{1, 2, 3}},
94+
}
95+
96+
for _, tt := range tests {
97+
t.Run(tt.name, func(t *testing.T) {
98+
result := slice.OrAlloc(tt.input, tt.n)
99+
if !reflect.DeepEqual(result, tt.expected) {
100+
t.Errorf("OrAlloc(%v, %d) = %v, want %v", tt.input, tt.n, result, tt.expected)
101+
}
102+
})
103+
}
104+
}
105+
106+
func TestReorderInPlace(t *testing.T) {
107+
tests := []struct {
108+
name string
109+
input []int
110+
indices []int
111+
expected []int
112+
}{
113+
{"Empty slice", []int{}, []int{}, []int{}},
114+
{"No reordering", []int{1, 2, 3}, []int{0, 1, 2}, []int{1, 2, 3}},
115+
{"Simple reordering", []int{1, 2, 3}, []int{2, 0, 1}, []int{3, 1, 2}},
116+
{"Complex reordering", []int{1, 2, 3, 4, 5}, []int{4, 3, 2, 1, 0}, []int{5, 4, 3, 2, 1}},
117+
}
118+
119+
for _, tt := range tests {
120+
t.Run(tt.name, func(t *testing.T) {
121+
input := make([]int, len(tt.input))
122+
copy(input, tt.input)
123+
slice.ReorderInPlace(func(i, j int) {
124+
input[i], input[j] = input[j], input[i]
125+
}, tt.indices)
126+
if !reflect.DeepEqual(input, tt.expected) {
127+
t.Errorf("ReorderInPlace() result = %v, want %v", input, tt.expected)
128+
}
129+
})
130+
}
131+
}

0 commit comments

Comments
 (0)